整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Canvas和WebGL,一個有趣、免費和快速的開源H5游戲框架—Phaser

          Phaser是一個有趣,免費和快速的2D游戲框架,用于制作桌面和移動web瀏覽器的HTML5游戲,支持Canvas和WebGL渲染。游戲可以通過第三方工具編譯到iOS, Android和本地應用。可以使用JavaScript或TypeScript進行開發。






          Github

          https://github.com/photonstorm/phaser

          特性

          除了出色的開源社區,Phaser也由 Photon Storm積極開發和維護。由于其快速的支持和開發者友好的API, Phaser目前是GitHub上最受歡迎的游戲框架之一。

          • WebGL和Canvas支持

          Phaser在內部同時使用Canvas和WebGL渲染器,并且可以根據瀏覽器支持在它們之間自動交換。這允許在桌面和移動設備上快速渲染

          • 預加載

          將資產的加載簡化為一行代碼。圖像、聲音、Sprite Sheets、Tilemaps、JSON數據、XML—所有這些都會自動解析和處理,隨時可以在游戲中使用,并存儲在一個全局緩存中供游戲對象共享。

          • 物理系統

          Phaser支持3個物理系統:Arcade physics,一個非常輕的AABB庫,非常適合低功耗設備。Matter.js提供彈性和更高級的支持

          • Sprites

          Sprites是游戲的生命之血。定位它們,在它們之間,旋轉它們,縮放它們,為它們設置動畫,碰撞它們,將它們繪制到自定義紋理上等等。Sprites也有完全的輸入支持:點擊他們,觸摸他們,拖動他們,捕捉他們-甚至像素完美的點擊檢測

          • 分組

          將大量的Sprites組合在一起,以便于共享和回收,避免不斷地創建對象。分組也可以發生沖突:例如,一個“子彈”組檢查與“外星人”組的沖突,并使用自定義的沖突回調來處理結果。

          • 動畫

          Phaser支持具有固定幀大小的經典Sprite Sheets以及幾種常見的紋理圖集格式,包括texture Packer、Starling和Unity YAML。所有這些都可以用來輕松地創建動畫。

          • 粒子

          粒子系統是內置的,它允許你輕松地創建有趣的粒子效果。創建爆炸或持續的流效果,如雨或火。或者將發射器附加到精靈上以獲得噴射軌跡。

          • 攝像頭

          具有先進的多攝像頭支持。輕松創建額外的攝像頭,然后在屏幕上的任何位置定位和縮放它們。相機可以滾動,也有特殊效果,如震動,閃光和褪色。四處搖攝,輕松跟隨精靈。

          • 輸入系統

          如果鼠標不停地在屏幕上切換,即使是鼠標也無法切換。觸摸,鼠標,鍵盤,游戲板和許多有用的功能,讓你可以制作任何你需要的輸入系統

          • 音頻系統

          Phaser同時支持Web音頻和傳統HTML音頻。

          • tilemap

          Phaser只需幾行代碼就可以加載、渲染和與tilemap相沖突。我們、、支持多個平鋪層的CSV和平鋪地圖數據格式。有很多強大的圖塊操作功能:交換、替換、刪除、添加和實時更新地圖

          • 設備縮放

          Phaser 2有一個內置的縮放管理器,允許你縮放你的游戲,以適應任何大小的屏幕。控制寬高比、最小和最大刻度以及全屏支持(即將推出Phaser 3)

          • 插件系統

          可以利用插件來解決一些基本問題之外的問題

          • 移動瀏覽器

          Phaser是專門為移動web瀏覽器而構建的。當然,它在桌面上運行的速度也非常快!



          如何使用

          npm install phaser

          或者使用cdn

          • 示例
          <!DOCTYPE html>
          <html>
          <head>
              <script src="./dist/phaser-arcade-physics.min.js"></script> 
          </head>
          <body>
          
              <script></script>
          
          </body>
          </html>
          var config = {
              type: Phaser.AUTO,
              width: 800,
              height: 600,
              physics: {
                  default: 'arcade',
                  arcade: {
                      gravity: { y: 200 }
                  }
              },
              scene: {
                  preload: preload,
                  create: create
              }
          };
          
          var game = new Phaser.Game(config);
          
          function preload ()
          {
              this.load.setBaseURL('http://***');
          
              this.load.image('sky', 'assets/skies/space3.png');
              this.load.image('logo', 'assets/sprites/phaser3-logo.png');
              this.load.image('red', 'assets/particles/red.png');
          }
          function create ()
          {
              this.add.image(400, 300, 'sky');
          
              var particles = this.add.particles('red');
          
              var emitter = particles.createEmitter({
                  speed: 100,
                  scale: { start: 1, end: 0 },
                  blendMode: 'ADD'
              });
          
              var logo = this.physics.add.image(400, 100, 'logo');
          
              logo.setVelocity(100, 200);
              logo.setBounce(1, 1);
              logo.setCollideWorldBounds(true);
          
              emitter.startFollow(logo);
          }


          總結

          Phaser是一個非常強大的2D游戲引擎框架,感興趣的小伙伴不要錯過啦!

          學lufylegend.js之日,我用lufylegend.js開發了第一個HTML5小游戲——拼圖游戲,還寫了篇博文來炫耀一下:HTML5小游戲《智力大拼圖》發布,挑戰你的思維風暴。不過當時初學游戲開發,經驗淺薄,所以沒有好好專研游戲里的算法和代碼的缺陷,導致游戲出現了很多bug,甚至拼圖打亂后很可能無法復原。最近經常有朋友問起這個游戲,希望我能把代碼里的bug改一下方便初學者學習,順便我也打算測試一下自己寫這種小游戲的速度,所以就抽出了一些時間將這個游戲從頭到尾重新寫了一遍,計算了一下用時,從準備、修改素材到最后完成游戲,一共用了大約2h的時間。

          以下是游戲地址:

          由于頭條禁止在文章頁面加入鏈接,大家私信我“拼圖”即可獲取下載地址。

          這是我的游戲記錄,歡迎各位挑戰:

          接下來就來講講如何開發完成這款游戲的。(按“編年體”)

          準備階段

          準備lufylegend游戲引擎,大家可以去官方網站下載:

          由于頭條禁止在文章頁面加入鏈接,大家私信我“拼圖”即可獲取下載地址。

          引擎文檔地址:

          由于頭條禁止在文章頁面加入鏈接,大家私信我“拼圖”即可獲取下載地址。

          可以說,如果沒有強大的lufylegend引擎,這種html5小游戲用原生canvas制作,少說要一天呢。

          0~30min

          準備素材(10min) + 修改素材(20min)。由于在下實在手殘,不善于P圖,修改圖片用了大約20min,囧……

          30~50min

          開發開始界面。游戲不能沒有開始界面所以我們首先實現這部分代碼。在此之前是index.html里的代碼,代碼如下:

          <!DOCTYPE html>

          <html>

          <head>

          <title>Puzzle</title>

          <meta charset="utf-8">

          <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

          <script type="text/javascript" src="./lib/lufylegend-1.10.1.simple.min.js"></script>

          <script type="text/javascript" src="./js/Main.js"></script>

          </head>

          <body style="margin: 0px; font-size: 0px; background: #F2F2F2;">

          <div id="mygame"></div>

          </body>

          </html>

          主要是引入一些js文件,不多說。然后準備一個Main.js文件,在這個文件里添加初始化界面和加載資源的代碼:

          /** 初始化游戲 */

          LInit(60, "mygame", 390, 580, main);

          var imgBmpd;

          /** 游戲層 */

          var stageLayer, gameLayer, overLayer;

          /** 拼圖塊列表 */

          var blockList;

          /** 是否游戲結束 */

          var isGameOver;

          /** 用時 */

          var startTime, time, timeTxt;

          /** 步數 */

          var steps, stepsTxt;

          function main () {

          /** 全屏設置 */

          if (LGlobal.mobile) {

          LGlobal.stageScale = LStageScaleMode.SHOW_ALL;

          }

          LGlobal.screen(LGlobal.FULL_SCREEN);

          /** 添加加載提示 */

          var loadingHint = new LTextField();

          loadingHint.text = "資源加載中……";

          loadingHint.size = 20;

          loadingHint.x = (LGlobal.width - loadingHint.getWidth()) / 2;

          loadingHint.y = (LGlobal.height - loadingHint.getHeight()) / 2;

          addChild(loadingHint);

          /** 加載圖片 */

          LLoadManage.load(

          [

          {path : "./js/Block.js"},

          {name : "img", path : "./images/img.jpg"}

          ],

          null,

          function (result) {

          /** 移除加載提示 */

          loadingHint.remove();

          /** 保存位圖數據,方便后續使用 */

          imgBmpd = new LBitmapData(result["img"]);

          gameInit();

          }

          );

          }

          function gameInit (e) {

          /** 初始化舞臺層 */

          stageLayer = new LSprite();

          stageLayer.graphics.drawRect(0, "", [0, 0, LGlobal.width, LGlobal.height], true, "#EFEFEF");

          addChild(stageLayer);

          /** 初始化游戲層 */

          gameLayer = new LSprite();

          stageLayer.addChild(gameLayer);

          /** 初始化最上層 */

          overLayer = new LSprite();

          stageLayer.addChild(overLayer);

          /** 添加開始界面 */

          addBeginningUI();

          }

          以上代碼有詳細注釋,大家可以對照引擎文檔和注釋進行閱讀。有些全局變量會在以后的代碼中使用,大家可以先忽略。接下來是addBeginningUI函數里的代碼,用于實現開始界面:

          function addBeginningUI () {

          var beginningLayer = new LSprite();

          beginningLayer.graphics.drawRect(0, "", [0, 0, LGlobal.width, LGlobal.height], true, "#EDEDED");

          stageLayer.addChild(beginningLayer);

          /** 游戲標題 */

          var title = new LTextField();

          title.text = "拼圖游戲";

          title.size = 50;

          title.weight = "bold";

          title.x = (LGlobal.width - title.getWidth()) / 2;

          title.y = 160;

          title.color = "#FFFFFF";

          title.lineWidth = 5;

          title.lineColor = "#000000";

          title.stroke = true;

          beginningLayer.addChild(title);

          /** 開始游戲提示 */

          var hint = new LTextField();

          hint.text = "- 點擊屏幕開始游戲 -";

          hint.size = 25;

          hint.x = (LGlobal.width - hint.getWidth()) / 2;

          hint.y = 370;

          beginningLayer.addChild(hint);

          /** 開始游戲 */

          beginningLayer.addEventListener(LMouseEvent.MOUSE_UP, function () {

          beginningLayer.remove();

          startGame();

          });

          }

          到此,運行代碼,得到我們的開始界面:

          看到這個畫面,其實我自己都想吐槽一下實在是太“樸素”了,囧……

          不過我這次圖個制作速度,所以還望各位看官海量。

          50~90min

          這40分鐘的時間,是最關鍵時期,期間我們要完成整個游戲的主體部分。首先,我們需要用代碼來實現以下過程:

          初始化游戲界面數據(如游戲時間、所用步數)和顯示一些UI部件(如圖樣)

          |

          -> 獲取隨機的拼圖塊位置

          |

          -> 顯示打亂后的拼圖塊

          我們將這些步驟做成一個個的函數方便我們統一調用:

          function startGame () {

          isGameOver = false;

          /** 初始化時間和步數 */

          startTime = (new Date()).getTime();

          time = 0;

          steps = 0;

          /** 初始化拼圖塊列表 */

          initBlockList();

          /** 打亂拼圖 */

          getRandomBlockList();

          /** 顯示拼圖 */

          showBlock();

          /** 顯示縮略圖 */

          showThumbnail();

          /** 顯示時間 */

          addTimeTxt();

          /** 顯示步數 */

          addStepsTxt();

          stageLayer.addEventListener(LEvent.ENTER_FRAME, onFrame);

          }

          函數一開始,我們把isGameOver變量設定為false代表游戲未結束,在后期的代碼里,我們會看到這個變量的作用。接著我們初始化了用于表示時間和步數的time和steps這兩個全局變量,另外初始化變量startTime的值用于后面計算游戲時間。

          接下來,我們就要開始初始化拼圖塊了。見initBlockList里的代碼:

          function initBlockList () {

          blockList = new Array();

          for (var i = 0; i < 9; i++) {

          /** 根據序號計算拼圖塊圖片顯示位置 */

          var y = (i / 3) >>> 0, x = i % 3;

          blockList.push(new Block(i, x, y));

          }

          }

          這里我們使用了一個Block類,這個類用于顯示拼圖塊和儲存拼圖塊的數據,并提供了一些方法來操控拼圖塊,下面是其構造器的代碼:

          function Block (index, x, y) {

          LExtends(this, LSprite, []);

          var bmpd = imgBmpd.clone();

          bmpd.setProperties(x * 130, y * 130, 130, 130);

          this.bmp = new LBitmap(bmpd);

          this.addChild(this.bmp);

          var border = new LShape();

          border.graphics.drawRect(3, "#CCCCCC", [0, 0, 130, 130]);

          this.addChild(border);

          this.index = index;

          this.addEventListener(LMouseEvent.MOUSE_UP, this.onClick);

          }

          Block類繼承自LSprite,屬于一個顯示對象,所以我們在這個類中添加了一個位圖對象用于顯示拼圖塊對應的圖片。除此之外,我們還為拼圖塊添加了一個邊框,在顯示時用于隔開周圍的拼圖塊。Block類有一個index屬性,代表拼圖塊在拼圖塊列表blockList中的正確位置。最后,我們為此類添加了一個鼠標按下事件,用于處理鼠標按下后移動圖塊操作。

          接下來我們還要介紹這個類的一個方法setLocation:

          Block.prototype.setLocation = function (x, y) {

          this.locationX = x;

          this.locationY = y;

          this.x = x * 130;

          this.y = y * 130;

          };

          這個方法用于設置拼圖塊對象的顯示位置以及保存拼圖塊的“數組位置”。什么是“數組位置”呢?各位看官可以通過下面的圖片加以了解:

          可以看到,“數組位置”就類似于二維數組中的元素下標。儲存這個位置的作用在于可以很方便地從blockList中獲取到附近的其他拼圖塊。這個方法在我們顯示拼圖時有調用到,在顯示拼圖之前,我們得先打亂拼圖,見如下代碼:

          function getRandomBlockList () {

          /** 隨機打亂拼圖 */

          blockList.sort(function () {

          return 0.5 - Math.random();

          });

          /** 計算逆序和 */

          var reverseAmount = 0;

          for (var i = 0, l = blockList.length; i < l; i++) {

          var currentBlock = blockList[i];

          for (var j = i + 1; j < l; j++) {

          var comparedBlock = blockList[j];

          if (comparedBlock.index < currentBlock.index) {

          reverseAmount++;

          }

          }

          }

          /** 檢測打亂后是否可還原 */

          if (reverseAmount % 2 != 0) {

          /** 不合格,重新打亂 */

          getRandomBlockList();

          }

          }

          打亂拼圖部分直接用數組的sort方法進行隨機打亂:

          blockList.sort(function () {

          return 0.5 - Math.random();

          });

          其實打亂算法有很多種,我這里采用最粗暴的方法,也就是隨機打亂。這種算法簡單是簡單,壞在可能出現無法復原的現象。針對這個問題,就有配套的檢測打亂后是否可還原的算法,具體的算法理論我借用lufy大神的評論:

          此類游戲能否還原關鍵是看它打亂后的逆序次數之和是否為偶數

          假設你打亂后的數組中的每一個小圖塊為obj0,obj1,obj2,…它們打亂之前的序號分別為obj0.num,obj1.num…

          接下來循環數組,如果前面元素的序號比此元素后某個元素的序號大,如obj0.num > obj1.num或者obj2.num > obj4.num就表示一個逆序

          當全部的逆序之和為奇數時表示不可還原,重新打亂即可,打亂后重新檢測,直到逆序之和為偶數為止

          舉個例子,如果有一個數組為[3, 4, 2, 1],那么里面3 2, 3 1, 2 4, 4 1, 2 1是逆序的,所以逆序數是5。

          上面我給出的getRandomBlockList里的代碼就是在實現打亂算法和檢測是否可還原算法。

          還有一種打亂方式,大家可以嘗試嘗試:和復原拼圖一樣,將空白塊一步一步地與周圍的拼圖隨機交換順序。這個打亂算法較上一種而言,不會出現無法復原的現象,而且可以根據打亂的步數設定游戲難度。

          在完成打亂拼圖塊后,如期而至的是顯示拼圖塊:

          function showBlock() {

          for (var i = 0, l = blockList.length; i < l; i++) {

          var b = blockList[i];

          /** 根據序號計算拼圖塊位置 */

          var y = (i / 3) >>> 0, x = i % 3;

          b.setLocation(x, y);

          gameLayer.addChild(b);

          }

          }

          顯示了拼圖塊后,我們要做的就是添加操作拼圖塊的功能。于是需要拓展Block類,為其添加事件監聽器onClick方法:

          Block.prototype.onClick = function (e) {

          var self = e.currentTarget;

          if (isGameOver) {

          return;

          }

          var checkList = new Array();

          /** 判斷右側是否有方塊 */

          if (self.locationX > 0) {

          checkList.push(Block.getBlock(self.locationX - 1, self.locationY));

          }

          /** 判斷左側是否有方塊 */

          if (self.locationX < 2) {

          checkList.push(Block.getBlock(self.locationX + 1, self.locationY));

          }

          /** 判斷上方是否有方塊 */

          if (self.locationY > 0) {

          checkList.push(Block.getBlock(self.locationX, self.locationY - 1));

          }

          /** 判斷下方是否有方塊 */

          if (self.locationY < 2) {

          checkList.push(Block.getBlock(self.locationX, self.locationY + 1));

          }

          for (var i = 0, l = checkList.length; i < l; i++) {

          var checkO = checkList[i];

          /** 判斷是否是空白拼圖塊 */

          if (checkO.index == 8) {

          steps++;

          updateStepsTxt();

          Block.exchangePosition(self, checkO);

          break;

          }

          }

          };

          首先,我們在這里看到了isGameOver全局變量的作用,即在游戲結束后,阻斷點擊拼圖塊后的操作。

          在點擊了拼圖塊后,我們先獲取該拼圖塊周圍的拼圖塊,并將它們裝入checkList,再遍歷checkList,當判斷到周圍有空白拼圖塊后,即周圍有index屬性等于8的拼圖塊后,先更新操作步數,然后將這兩個拼圖塊交換位置。具體交換拼圖塊位置的方法詳見如下代碼:

          Block.exchangePosition = function (b1, b2) {

          var b1x = b1.locationX, b1y = b1.locationY,

          b2x = b2.locationX, b2y = b2.locationY,

          b1Index = b1y * 3 + b1x,

          b2Index = b2y * 3 + b2x;

          /** 在地圖塊數組中交換兩者位置 */

          blockList.splice(b1Index, 1, b2);

          blockList.splice(b2Index, 1, b1);

          /** 交換兩者顯示位置 */

          b1.setLocation(b2x, b2y);

          b2.setLocation(b1x, b1y);

          /** 判斷游戲是否結束 */

          Block.isGameOver();

          };

          還有就是Block.getBlock靜態方法,用于獲取給定的“數組位置”下的拼圖塊:

          Block.getBlock = function (x, y) {

          return blockList[y * 3 + x];

          };

          在Block.exchangePosition中,我們通過Block.isGameOver判斷玩家是否已將拼圖復原:

          Block.isGameOver = function () {

          var reductionAmount = 0, l = blockList.length;

          /** 計算還原度 */

          for (var i = 0; i < l; i++) {

          var b = blockList[i];

          if (b.index == i) {

          reductionAmount++;

          }

          }

          /** 計算是否完全還原 */

          if (reductionAmount == l) {

          /** 游戲結束 */

          gameOver();

          }

          };

          到這里,我們就實現了打亂和操作拼圖塊部分。

          90~120min

          最后30min用于細枝末節上的處理,如顯示拼圖縮略圖、顯示&更新時間和步數,以及添加游戲結束畫面,這些就交給如下冗長而簡單的代碼來完成吧:

          function showThumbnail() {

          var thumbnail = new LBitmap(imgBmpd);

          thumbnail.scaleX = 130 / imgBmpd.width;

          thumbnail.scaleY = 130 / imgBmpd.height;

          thumbnail.x = (LGlobal.width - 100) /2;

          thumbnail.y = 410;

          overLayer.addChild(thumbnail);

          }

          function addTimeTxt () {

          timeTxt = new LTextField();

          timeTxt.stroke = true;

          timeTxt.lineWidth = 3;

          timeTxt.lineColor = "#54D9EF";

          timeTxt.color = "#FFFFFF";

          timeTxt.size = 18;

          timeTxt.x = 20;

          timeTxt.y = 450;

          overLayer.addChild(timeTxt);

          updateTimeTxt();

          }

          function updateTimeTxt () {

          timeTxt.text = "時間:" + getTimeTxt(time);

          }

          function getTimeTxt () {

          var d = new Date(time);

          return d.getMinutes() + " : " + d.getSeconds();

          };

          function addStepsTxt () {

          stepsTxt = new LTextField();

          stepsTxt.stroke = true;

          stepsTxt.lineWidth = 3;

          stepsTxt.lineColor = "#54D9EF";

          stepsTxt.color = "#FFFFFF";

          stepsTxt.size = 18;

          stepsTxt.y = 450;

          overLayer.addChild(stepsTxt);

          updateStepsTxt();

          }

          function updateStepsTxt () {

          stepsTxt.text = "步數:" + steps;

          stepsTxt.x = LGlobal.width - stepsTxt.getWidth() - 20;

          }

          function onFrame () {

          if (isGameOver) {

          return;

          }

          /** 獲取當前時間 */

          var currentTime = (new Date()).getTime();

          /** 計算使用的時間并更新時間顯示 */

          time = currentTime - startTime;

          updateTimeTxt();

          }

          function gameOver () {

          isGameOver = true;

          var resultLayer = new LSprite();

          resultLayer.filters = [new LDropShadowFilter()];

          resultLayer.graphics.drawRoundRect(3, "#BBBBBB", [0, 0, 350, 350, 5], true,"#DDDDDD");

          resultLayer.x = (LGlobal.width - resultLayer.getWidth()) / 2;

          resultLayer.y = LGlobal.height / 2;

          resultLayer.alpha = 0;

          overLayer.addChild(resultLayer);

          var title = new LTextField();

          title.text = "游戲通關"

          title.weight = "bold";

          title.stroke = true;

          title.lineWidth = 3;

          title.lineColor = "#555555";

          title.size = 30;

          title.color = "#FFFFFF";

          title.x = (resultLayer.getWidth() - title.getWidth()) / 2;

          title.y = 30;

          resultLayer.addChild(title);

          var usedTimeTxt = new LTextField();

          usedTimeTxt.text = "游戲用時:" + getTimeTxt(time);

          usedTimeTxt.size = 20;

          usedTimeTxt.stroke = true;

          usedTimeTxt.lineWidth = 2;

          usedTimeTxt.lineColor = "#555555";

          usedTimeTxt.color = "#FFFFFF";

          usedTimeTxt.x = (resultLayer.getWidth() - usedTimeTxt.getWidth()) / 2;

          usedTimeTxt.y = 130;

          resultLayer.addChild(usedTimeTxt);

          var usedStepsTxt = new LTextField();

          usedStepsTxt.text = "所用步數:" + steps;

          usedStepsTxt.size = 20;

          usedStepsTxt.stroke = true;

          usedStepsTxt.lineWidth = 2;

          usedStepsTxt.lineColor = "#555555";

          usedStepsTxt.color = "#FFFFFF";

          usedStepsTxt.x = usedTimeTxt.x;

          usedStepsTxt.y = 180;

          resultLayer.addChild(usedStepsTxt);

          var hintTxt = new LTextField();

          hintTxt.text = "- 點擊屏幕重新開始 -";

          hintTxt.size = 23;

          hintTxt.stroke = true;

          hintTxt.lineWidth = 2;

          hintTxt.lineColor = "#888888";

          hintTxt.color = "#FFFFFF";

          hintTxt.x = (resultLayer.getWidth() - hintTxt.getWidth()) / 2;

          hintTxt.y = 260;

          resultLayer.addChild(hintTxt);

          LTweenLite.to(resultLayer, 0.5, {

          alpha : 0.7,

          y : (LGlobal.height - resultLayer.getHeight()) / 2,

          onComplete : function () {

          /** 點擊界面重新開始游戲 */

          stageLayer.addEventListener(LMouseEvent.MOUSE_UP, function () {

          gameLayer.removeAllChild();

          overLayer.removeAllChild();

          stageLayer.removeAllEventListener();

          startGame();

          });

          }

          });

          }

          Ok,2h下來,整個游戲就搞定咯~不得不表揚一下lufylegend這個游戲引擎,實在是可以大幅提升開發效率。

          源代碼下載

          最后奉上源代碼:

          由于頭條禁止在文章頁面加入鏈接,大家私信我“拼圖”即可獲取下載地址。

          致謝與反思

          這篇博文在最初寫成的時候,我沒有對逆序算法進行深入研究,再加上我的測試不仔細,我沒有發現算法的錯誤之處。因此,在博文發布后,不少讀者發現游戲無解現象并將此問題反饋給了我,經過網友熱心幫助,我才找到了問題所在,并更正了算法。在此對這些熱心的網友表示真心的感謝,也為我學習不深入,以及誤導了不少讀者而感到十分內疚自責。

          如果大家對本文有任何意見或不解,歡迎留言~

          于工作的需要,需要對頁面進行投屏操作,這時為了達到更好的效果,瀏覽器全屏顯示類似F12的效果。 HTML5提供了操作瀏覽器全屏的API,目前google chrome 15 +, safri5.1+,firfox10+,IE1能夠很好的支持該屬性;低版本的IE可以通過ActiveX插件實現; google chrome 15 +, safri5.1+,firfox10+,IE1實現方式: 打開全屏:

          退出全屏:

          注意事項: 1,如果是低版本的在IE11以下,通過ActiveXObject ,首次加載頁面的時候會出現是否加載AxtiveX控件的提示,這里會有一點不太友好,如果要避免這樣的提示,只能去選項/設置面板中修改IE關于activex 控件的配置。沒辦法,這就是為什么這么多人討厭IE的原因,但是IE還素有一個好處的,下面我們會講到。 2,chrome等高版本瀏覽器可以通過調用html5提供的API實現全屏和退出全屏,但是這個動作必須手動出發,例如通過點擊事件:

          $(document).onclick(function(){ fullScreen(); })

          這樣是可以實現,但是如果我們需要在不手動觸發的情況下通過js代碼實現全屏API提供的方法就無法實現,如:

          $(function(){ fullScreen(); })//或者$("document").trigger('click');

          上面這樣瀏覽會報警告,提示:’Failed to execute ‘requestFullScreen’ on ‘Element’: API can only be initiated by a user gesture’,意思大概就是該方法只能通過手勢出發。但是低版本的IE通過 wscript.SendKeys(“{F11}”)是可以實現的。 所以IE是又讓人愛又讓人恨,當然恨多一點。


          主站蜘蛛池模板: 日韩精品中文字幕视频一区| 人妻免费一区二区三区最新| 国产乱码精品一区二区三区麻豆| 夜夜添无码一区二区三区| 加勒比精品久久一区二区三区| 日韩av片无码一区二区不卡电影| 韩国福利影视一区二区三区| 精品一区二区三区在线播放视频 | 亚洲日韩精品一区二区三区| 亚洲人成网站18禁止一区| 亚洲av成人一区二区三区在线观看| 国产传媒一区二区三区呀| 美女视频一区二区| 精品国产一区二区22| 国产精品99无码一区二区| 国产午夜精品一区二区三区| 亚洲AV综合色区无码一区爱AV| 台湾无码一区二区| 亚洲国产欧美国产综合一区 | 亚洲欧美国产国产一区二区三区| 中文字幕一区在线观看视频| 亚洲熟女综合色一区二区三区| 国产微拍精品一区二区| 北岛玲在线一区二区| 国产伦精品一区二区免费| 国产午夜精品免费一区二区三区 | 在线精品国产一区二区| 2021国产精品视频一区| 国产成人一区二区三区高清| 一区二区中文字幕在线观看| 真实国产乱子伦精品一区二区三区 | 精产国品一区二区三产区| 国产伦精品一区二区三区免费迷 | 国产一区玩具在线观看| 88国产精品视频一区二区三区| 亚洲AV无码一区二区大桥未久| 一区二区三区内射美女毛片 | 无码精品国产一区二区三区免费 | 亚洲爆乳无码一区二区三区| 亚洲宅男精品一区在线观看| 国产乱码精品一区二区三区四川 |