溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

發(fā)布時間:2022-02-23 11:28:40 來源:億速云 閱讀:240 作者:小新 欄目:開發(fā)技術

小編給大家分享一下在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

    canvas基礎知識

    畫布元素

    canvas標簽可以讓我們能夠使用JavaScript在網(wǎng)頁上繪制各種樣式的圖形。要訪問實際的繪圖接口, 首先我們需要創(chuàng)建一個上下文(context), 它是一個對象, 提供了繪圖的接口。目前有兩種廣受繪圖的樣式: 用于二維圖形的”2d“以及通過 OpenGL 接口的三維圖形的 webgl 。

    比如, 我們可以使用 <canvas /> DOM元素上的 getContext 方法創(chuàng)建上下文。

     <body>
       <canvas width="500" height="500" />
     </body>
     <script>
       let canvas = document.querySelector('canvas');
       let context = canvas.getContext('2d');
       context.fillStyle = "yellow";
       context.fillRect(10, 10, 400, 400);
     </script>

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    我們繪制了一個寬度和高度都為400像素的黃色正方形, 并且其左上角頂點處的坐標為(10, 10)。canvas的坐標系(0, 0)在其左上角.

    邊框的繪制

    在畫布的接口中, fillRect 方法用于填充矩形。 fillStyle 用于控制填充形狀的方法。比如

    單色:

    context.fillStyle = "yellow";

    漸變色:

    let canvas = document.querySelector('canvas');
    let context = canvas.getContext('2d');
    let grd = context.createLinearGradient(0,0,170,0);
    grd.addColorStop(0,"black");
    grd.addColorStop(1,"red");
    context.fillStyle = grd;
    context.fillRect(10, 10, 400, 400);

    pattern圖案對象:

    let canvas = document.querySelector('canvas');
    let context = canvas.getContext('2d');
    let img = document.createElement('img');
    img.src = "https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3112798566,2640650199&fm=26&gp=0.jpg";
    img.onload = () => {
      let pattern = context.createPattern(img, 'no-repeat');
      context.fillStyle = pattern;
      context.fillRect(10,10,400,400)
    }

    strokeStyle屬性與fillStyle屬性類似, 但是 strokeStyle 作用與描邊線的顏色。線條的寬度由 lineWidth 屬性決定。

    比如我想繪制一個邊框寬度為6的黃色正方形。

    let canvas = document.querySelector('canvas');
    let context = canvas.getContext('2d');
    context.strokeStyle = "yellow";
    context.lineWidth = 6;
    context.strokeRect(10,10, 400, 400);

    路徑

    路徑是很多線條的組合。如果想要繪制各種各樣的形狀,我們會頻繁用到 moveTolineTo 兩個函數(shù)。

      let canvas = document.querySelector('canvas');
      let context = canvas.getContext('2d');
      context.beginPath();
      for (let index = 0; index < 400; index+=10) {
        context.moveTo(10, index);
        context.moveTo(index, 0);
        context.lineTo(390, index);
      }
      context.stroke();

    moveTo 表示我們當前畫筆起點的位置, lineTo 表示我們畫筆從起點到終點的連線。以上代碼執(zhí)行后就是如下所示:

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    當然我們可以為線條繪制的圖形進行填充。

      let canvas = document.querySelector('canvas');
      let context = canvas.getContext('2d');
      context.beginPath();
      context.moveTo(50, 10);
      context.lineTo(10, 70);
      context.lineTo(90, 70);
      context.fill();
      context.closePath();

    繪制圖片

    在計算機圖形學中, 通常需要對矢量圖形和位圖圖形進行區(qū)分。 矢量圖形是指: 通過給出形狀的邏輯來描述指定的圖片。而位圖圖形是指使用像素數(shù)據(jù), 而不指定實際形狀。

    canvas中的 drawImage 方法允許我們將像素數(shù)據(jù)繪制到畫布上。像素的數(shù)據(jù)可以來自于元素或者另外一個畫布。

    drawImage支持傳遞9個參數(shù), 第2到5個參數(shù)表明源圖像中被復制的(x, y, 高度, 寬度), 第6到9個參數(shù)給出被復制的圖像在canvas畫布上的位置以及寬高。

    下圖是瑪麗多個姿勢的匯總圖, 我們使用 drawImage 先讓他能夠正常跑起來。

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    let canvas = document.querySelector('canvas');
    let ctx = canvas.getContext('2d');
    let img = document.createElement('img');
    img.src = './player_big.png'
    let spriteW = 47, spriteH = 58;
    img.onload = () => {
      let cycle = 0;
      setInterval(() => {
        ctx.clearRect(0, 0, spriteW, spriteH);
        ctx.drawImage(img,
         cycle*spriteW, 0, spriteW, spriteH,
         0, 0, spriteW, spriteH,
        );
        cycle = (cycle + 1) % 10;
      }, 120);
    }

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    我們需要大致截取瑪麗的大小, 通過 cycle 鎖定瑪麗在動畫中的位置。在合成中, 我們只需要讓前面8個動作循環(huán)播放即可實現(xiàn)瑪麗的一個奔跑動作了。

    控制轉換

    現(xiàn)在我們已經(jīng)可以讓瑪麗朝著右邊跑了, 但是在實際的游戲中 瑪麗是可以左右跑的。這里的話 有兩個方案: 1. 我們再繪制一組朝著左邊跑的組合圖 2.控制畫布反過來繪制圖片。第一種方案比較簡單, 因此我們就選擇第二種比較復雜一點的方案。

    canvas中可以調(diào)用scale方法按照比例尺調(diào)整然后繪制。此方法有兩個參數(shù), 第一個參數(shù)用于設置水平方向比例尺, 另外一個設置垂直方向的比例尺。

    let canvas = document.querySelector('canvas');
    let ctx = canvas.getContext('2d');
    ctx.scale(3, .5);
    ctx.beginPath();
    ctx.arc(50, 50, 40, 0, 7);
    ctx.lineWidth = 3;
    ctx.stroke();

    上面是對 scale 的簡單應用。我們調(diào)用了 scale 使得圓的水平方向被拉伸了3倍, 垂直方向被縮小了0.5倍。

    如果scale中的參數(shù)為負數(shù)-1時, 在x位置為100的位置繪制的形狀最終會被繪制到-100的位置。因此為了轉化圖片, 我們不能僅僅在drawImage的之前調(diào)用 ctx.scale(-1, 1) , 因為在當前畫布中是看不到轉化后的圖片的。這里有兩種方案: 1. 調(diào)用 drawImage 的時候設置x為-50的時候來繪制圖形 2.通過調(diào)整坐標軸, 這種做法的好處在于我們編寫的繪圖不需要關心比例尺的變化。

    我們采用 rotate 來渲染繪制的圖形, 并且通過 translate 方法移動他們。

      function flip(context, around) {
        context.translate(around, 0);
        context.scale(-1, 1);
        context.translate(-around, 0);
      }

    我們的思路大概是這樣子:

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    如果我們在正x處繪制三角形, 默認情況下它會位于1位置。調(diào)用flip函數(shù)后首先進行右邊平移, 得到三角形2. 然后通過調(diào)用 scale 進行翻轉得到三角形3。最后再次通過調(diào)用 translate 方法, 對三角形3進行平移得到三角形4, 也就是最后我們想要的圖案。

     let canvas = document.querySelector('canvas');
      let ctx = canvas.getContext('2d');
      let img = document.createElement('img');
      img.src = './player_big.png'
      let spriteW = 47, spriteH = 58;
      img.onload = () => {
          ctx.clearRect(100, 0, spriteW, spriteH);
          flip(ctx, 100 + spriteW / 2);
          ctx.drawImage(img,
          0, 0, spriteW, spriteH,
          100, 0, spriteW, spriteH,
          );
      }

    看, 他已經(jīng)被我們轉過來了!

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    升級超級瑪麗游戲

    在上一篇文章中, 我們所有的元素都是直接通過DOM來顯示的, 那么在我們學完canvas之后, 我們可以使用drawImage來繪制元素。

    我們定義CanvasDisplay替換掉之前的DOMDisplay, 除此之外, 我們新增了跟蹤自己視圖窗口, 他可以告訴我們當前正在那部分的關卡, 此外我還新增了 flipPlayer 屬性, 這樣即使瑪麗不動, 它仍然面對著它最后移動的方向。

    var CanvasDisplay = class CanvasDisplay {
      constructor(parent, level) {
        this.canvas = document.createElement("canvas");
        this.canvas.width = Math.min(600, level.width * scale);
        this.canvas.height = Math.min(450, level.height * scale);
        parent.appendChild(this.canvas);
        this.cx = this.canvas.getContext("2d");
    
        this.flipPlayer = false;
    
        this.viewport = {
          left: 0,
          top: 0,
          width: this.canvas.width / scale,
          height: this.canvas.height / scale
        };
      }
    
      clear() {
        this.canvas.remove();
      }
    }

    syncState方法首先計算新視圖窗口, 然后在適當?shù)奈恢美L制。

    CanvasDisplay.prototype.syncState = function(state) {
      this.updateViewport(state);
      this.clearDisplay(state.status);
      this.drawBackground(state.level);
      this.drawActors(state.actors);
    };
    DOMDisplay.prototype.syncState = function(state) {
      if (this.actorLayer) this.actorLayer.remove();
      this.actorLayer = drawActors(state.actors);
      this.dom.appendChild(this.actorLayer);
      this.dom.className = `game ${state.status}`;
      this.scrollPlayerIntoView(state);
    };

    在之前的更新相反, 我們現(xiàn)在必須在每次更新的時候, 重新繪制背景。因為畫布上的形狀只是像素, 所以我們在繪制完后沒有好的方法來移動或者刪除他們。因此更新畫布的唯一方法是清除并且重繪。

    updateViewport 方法跟 scrollPlayerIntoView 方法一樣。它會檢查玩家是否太靠近視圖邊緣。

    CanvasDisplay.prototype.updateViewport = function(state) {
      let view = this.viewport, margin = view.width / 3;
      let player = state.player;
      let center = player.pos.plus(player.size.times(0.5));
    
      if (center.x < view.left + margin) {
        view.left = Math.max(center.x - margin, 0);
      } else if (center.x > view.left + view.width - margin) {
        view.left = Math.min(center.x + margin - view.width,
                            state.level.width - view.width);
      }
      if (center.y < view.top + margin) {
        view.top = Math.max(center.y - margin, 0);
      } else if (center.y > view.top + view.height - margin) {
        view.top = Math.min(center.y + margin - view.height,
                            state.level.height - view.height);
      }
    };

    當我們成功或者失敗的時候, 我們需要清除當前場景, 因為如果失敗了, 我們需要重新來, 如果成功了, 我們需要刪除當前場景, 重新繪制一個新的場景。

    CanvasDisplay.prototype.clearDisplay = function(status) {
      if (status == "won") {
        this.cx.fillStyle = "rgb(68, 191, 255)";
      } else if (status == "lost") {
        this.cx.fillStyle = "rgb(44, 136, 214)";
      } else {
        this.cx.fillStyle = "rgb(52, 166, 251)";
      }
      this.cx.fillRect(0, 0,
                      this.canvas.width, this.canvas.height);
    };

    接下來, 我們需要繪制墻壁和熔巖。首先, 我們遍歷當前視圖中所有的墻壁和磚頭。我們使用 sprites.png 繪制所有非空的墻磚(墻、熔巖、金幣)。在提供的素材中, 我們墻壁是20px * 20px, 偏移量是0,熔巖也是 20px * 20px, 但是偏移量是20px.

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

    let otherSprites = document.createElement("img");
    otherSprites.src = "img/sprites.png";
    
    CanvasDisplay.prototype.drawBackground = function(level) {
      let {left, top, width, height} = this.viewport;
      let xStart = Math.floor(left);
      let xEnd = Math.ceil(left + width);
      let yStart = Math.floor(top);
      let yEnd = Math.ceil(top + height);
    
      for (let y = yStart; y < yEnd; y++) {
        for (let x = xStart; x < xEnd; x++) {
          let tile = level.rows[y][x];
          if (tile == "empty") continue;
          let screenX = (x - left) * scale;
          let screenY = (y - top) * scale;
          let tileX = tile == "lava" ? scale : 0;
          this.cx.drawImage(otherSprites,
                            tileX,         0, scale, scale,
                            screenX, screenY, scale, scale);
        }
      }
    };

    最后我們需要繪制玩家的模型。

    在前面的8個圖像中, 是一個完整的運動過程。第九個畫像是玩家靜止不動的狀態(tài), 第10個畫像是玩家在離地時候的狀態(tài)。因此當玩家移動的時候, 我們需要每60ms切換一幀。當玩家不動的時候繪制第九個畫面, 當玩家跳躍的時候繪制第十個畫面。

    在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲

     CanvasDisplay.prototype.drawPlayer = function(player, x, y,
                                                  width, height){
      width += playerXOverlap * 2;
      x -= playerXOverlap;
      if (player.speed.x != 0) {
        this.flipPlayer = player.speed.x < 0;
      }
    
      let tile = 8;
      if (player.speed.y != 0) {
        tile = 9;
      } else if (player.speed.x != 0) {
        tile = Math.floor(Date.now() / 60) % 8;
      }
    
      this.cx.save();
      if (this.flipPlayer) {
        flipHorizontally(this.cx, x + width / 2);
      }
      let tileX = tile * width;
      this.cx.drawImage(playerSprites, tileX, 0, width, height,
                                      x,     y, width, height);
      this.cx.restore();
    };

    對于不是玩家的模型, 我們根據(jù)對應模型的偏移量找到對應的圖像。

     CanvasDisplay.prototype.drawActors = function(actors) {
      for (let actor of actors) {
        let width = actor.size.x * scale;
        let height = actor.size.y * scale;
        let x = (actor.pos.x - this.viewport.left) * scale;
        let y = (actor.pos.y - this.viewport.top) * scale;
        if (actor.type === "player") {
          this.drawPlayer(actor, x, y, width, height);
        } else {
          let tileX = (actor.type === "coin" ? 2 : 1) * scale;
          this.cx.drawImage(otherSprites,
                            tileX, 0, width, height,
                            x,     y, width, height);
        }
       }
     };

    以上是“在html5中如何使用Canvas實現(xiàn)超級瑪麗游戲”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

    AI