本文章是由機器翻譯。

遊戲開發

Web 的 2D 遊戲引擎

Michael Oneppo

下載代碼示例

想像這世間並開發者想要寫一個遊戲。此開發人員環顧四周,看看哪些工具可用,並感到失望的是這些選項。所以,這個開發人員然後決定創建一個自訂的引擎,將滿足當前的遊戲概念的所有要求。它也將履行任何人將永遠不可能有的所有未來的遊戲開發要求。而且,因此,開發人員實際上從來不寫一個遊戲。

這是一個經常發生的悲劇。幸運的是,這個故事也催生出幾十個遊戲開發引擎的所有可能的平臺。這些包括方法、 工具和許可模式的範圍很廣。例如, JavaScripting.com 目前列出 14 專用打開原始程式碼遊戲庫。這還不包括物理引擎、 音訊庫和專門的輸入的系統,為遊戲。這樣的選擇,那裡了必定遊戲引擎,適合任何你想要的遊戲專案,將工作。

這篇文章將為 Web 走過三個流行的開源的 2D 遊戲引擎:狡猾,藍光和移相。比較和對比這些庫,我已經從我的第一篇文章移植坪遊戲 (msdn.microsoft.com/magazine/dn913185) 向每一個人,要看他們的感覺。請牢記,通過限制為 2D 的東西,我省去了大量的 3D 遊戲引擎的 web。我將涵蓋那些在未來。現在,我將重點對 2D 和那裡的機會。

狡猾

狡猾 (craftyjs.com) 是主要為基於瓷磚的遊戲,雖然它非常適合於各種各樣的 2D 遊戲,包括我的坪遊戲。Crafty 的基礎是它調用元件物件模型/依賴系統。

元件就像您定義具有特定的屬性和操作的類。每個元件的實例稱為實體。元件是為了沉重的混合,以使複雜、 功能豐富的實體。例如,如果你想要一個雪碧表在 Crafty,用雪碧函數來生成元件表示你的精靈。這裡是從圖像的雪碧類型是如何定義:

Crafty.sprite("sprites.png", {
  PlayerSprite: [0, 128, 128, 128],
  OpponentSprite: [0, 0, 128, 128],
  BallSprite: [128, 128, 64, 64],
  UpSprite: [128,0,64,64],
  DownSprite: [128,64,64,64],
  LeftSprite: [192,0,64,64],
  RightSprite: [192,64,64,64]});

現在,如果你想使一個雪碧工作表使用映射的球的球實體,包括 BallSprite 元件在創建實體時,"e",用函數,如下所示:

Crafty.e("Ball, BallSprite");

但這不會向螢幕繪製它。你要告訴你想要住在 2D (所以它會有一個"x"和"y"的位置),此實體的 Crafty 和你想要使用 DOM 元素繪製:

Crafty.e("Ball, 2D, DOM, BallSprite");

如果你想要在畫布上繪製元素,它是一樣簡單的文件物件模型 (DOM) 元件替換畫布。

實體的行為像獨立的物件在螢幕上對事件作出反應。像許多 JavaScript 庫,狡猾的實體有一個綁定函數來對事件作出反應。它是重要的是記住這些是 Crafty 特定的事件。例如,如果你想要做某事在每一幀中的實體,有了它對 EnterFrame 事件作出反應。這正是什麼需要四處移動球基於誰持有它和適當的物理,如果必要。圖 1 顯示球實體初始化同一個 EnterFrame 事件函數,它涵蓋了球的運動。

圖 1 球實體定義

Crafty.e("Ball, 2D, DOM, BallSprite, Collision")
  .attr({ x: width/2, y: height/2, velocity: [0,0], owner: 'User' })
  .bind('EnterFrame', function () {
    // If there's an owner, have the ball follow the owner.
    // Nudge the ball to the left or right of the player
    // so it doesn't overlap.
    if (this.owner) {
      var owner = Crafty(this.owner);
      switch(this.owner) {
        case 'User':
          this.x = owner.x + 128;
          break;
        case 'Opponent':
          this.x = owner.x - 64;
            }
            this.y = owner.y + 32;
            return;
    }
    // Bounce the ball off the ceiling or floor.
    if (this.y <= 0 || this.y >= (height - 64))
      this.velocity[1] = -this.velocity[1];
    // Move the ball based on its velocity, which is defined in
    // pixels per millisecond.
    var millis = 1000 / Crafty.timer.FPS();
    this.x += this.velocity[0] * millis;
    this.y += this.velocity[1] * millis;
  })

如果你仔細觀察,你會看到"this"經常用來指實體屬性。內置狡猾的"this.x"和"this.y"定義擊球的姿勢。當您創建球與 2D 的元件時,添加此功能。聲明,"this.velocity,"自訂我的代碼。你可以看到它定義為使用 attr 函數的屬性。也是如此與 this.owner,以找出如果或者球員拿著球。

狡猾的有幾個內建群組件,但我不使用它們大量在坪的示例。這裡有幾個:

  • 重力:狡猾有一個簡單的重力引擎,自動拉向下的實體。您可以定義任意數量的元素類型阻止其議案。
  • 雙向/FourWay/MultiWay 運動:通過這些元件,您獲得各種街機風格運動輸入方法。雙向是遊戲,與左,右和跳轉選項可綁定到任何你想要的鍵。FourWay 允許自上而下的運動中主要的方向。多路允許自訂的輸入,每個都有一個任意的方向。
  • 粒子:狡猾的包括一個快速粒子系統,您可以只使用繪圖畫布。作為柔和圓潤,單色圖像,顆粒特別適合煙、 火災和爆炸。

平,在 Crafty,執行的代碼是在本文附帶的代碼下載中可用。看一看要全貌的我如何移植的事情,特別是為對手 AI。

Pixi.js

Pixi.js (pixijs.com) 是裸機,結束對金屬 2D 網路渲染器。Pixi.js 可以放棄 2D 畫布和直潛入 WebGL,給它顯著的性能增益。Pixi.js 遊戲有兩個關鍵區段:描述你在一個場景圖的遊戲佈局階段和渲染器,實際上階段使用畫布或 WebGL 向螢幕繪製元素。預設情況下,Pixi.js 將使用 WebGL,如果可用:

$(document).ready(function() {
  // The stage to play your game.
  stage = new PIXI.Stage(0xFFFFFFFF);
  lastUpdate = 0;
  // autoDetectRenderer() finds the fastest renderer you've got.
  renderer = PIXI.autoDetectRenderer(innerWidth, innerHeight);
  document.body.appendChild(renderer.view);
});

Pixi.js 擁有的場景圖和轉換,但很重要,請注意您需要自己渲染場景。所以,我坪的遊戲,我把渲染迴圈會按如下方式使用 requestAnimationFrame:

function update(time) {
  var t = time - lastUpdate;
  lastUpdate = time;
  ball.update(t);
  ai.update();
  // New: You actually have to render the scene
  renderer.render(stage);
  requestAnimationFrame(update);
}

不像 Crafty,Pixi.js 並不規定如何構建你的遊戲,所以差不多可以稍作修改相同的代碼從原始的平比賽,很多。

您將看到最大的區別是 Pixi.js 也可以作為影像檔一個接一個地從載入精靈或作為平鋪的圖像使用一個特殊的工具稱為 TexturePacker。此工具將自動合併到一個單一、 優化檔隨附的 JSON 檔描述圖片的位置與一組單獨的圖像。我用 TexturePacker 來使雪碧表。圖 2 顯示了更新的準備好了函數,載入雪碧圖像。

圖 2 載入在 Pixi.js 的雪碧表

$(document).ready(function() {
  // Create an new instance of a Pixi stage.
  stage = new PIXI.Stage(0xFFFFFFFF);
  lastUpdate = 0;
  // Create a renderer instance.
  renderer = PIXI.autoDetectRenderer(innerWidth, innerHeight);
  document.body.appendChild(renderer.view);
  var images = ["sprites.json"];
  var loader = new PIXI.AssetLoader(images);
  loader.onComplete = startGame;
  loader.load();
});

裝載機是非同步因為它需要一個函數來呼籲完成載入。其餘的遊戲初始化,類似于創建球員、 對手和球,是在新的 startGame 函數。若要使用這些新初始化的精靈,請使用你上手。Sprite.from-­框架考慮到精靈在 JSON 檔中的陣列的索引的方法。

Pixi.js 提供的最後一個不同是觸摸操作和滑鼠的輸入的系統。若要使任何雪碧輸入-準備好了,我互動將屬性設置為"true",直接將某些事件處理常式添加到雪碧。例如,對於向上/向下按鈕,我一定要移動向上和向下的球員的事件如中所示圖 3

圖 3 綁定事件向上和向下移動遊戲玩家

up = new PIXI.Sprite.fromFrame(3);
up.interactive = true;
stage.addChild(up);
up.touchstart = up.mousedown = function() {
  player.move(-distance);
}
down = new PIXI.Sprite.fromFrame(4);
down.interactive = true;
stage.addChild(down);
down.touchstart = down.mousedown = function() {
  player.move(distance);
}

它是重要的是注意到我不得不手動添加到場景以使其可見的每個精靈。您可以檢查的出我的版本的坪建 Pixi.js 在本文附帶的代碼下載中。

相移器

相移器 (phaser.io) 是一個全功能的遊戲引擎,使用引擎蓋下的藍光來執行所有的繪製任務。移了一長串的功能,包括基於幀的雪碧動畫、 音樂和音訊,一種遊戲狀態系統和物理與三個不同的派遣庫。

因為我已經移植到 Pixi.js 上的坪,移植到相位是直截了當的。相移器簡化了基於 Pixi.js 的物件創建:

ball = game.add.sprite(x, y, 'sprites');

雖然相位可以更簡潔,有事情更複雜的地方。例如,在前面的代碼中,最後一個參數"雪碧"指的是在啟動時載入的映射:

game.load.image('sprites', 'sprites.png');

相移器有一個不同的雪碧表系統,來自 Pixi.js 的圖像。 你可以把圖像分成瓷磚。若要使用它,請調用這樣的事情:

game.load.spritesheet('sprites', 'mySprites.png',
  spriteWidth, spriteHeight, totalSprites);

比如說,我實際上不使用此函數。代碼我剛給你假定所有的精靈都是相同的大小,但坪雪碧板材具有 64 圖元和 128 圖元的精靈。因此,我不得不手動裁剪每個精靈的形象。若要裁剪下來的圖像的球,我在雪碧上設置裁剪矩形:

ball.cropRect = new Phaser.Rectangle(0, 64, 64, 64);
ball.updateCrop();

希望這顯示了一些相移器的靈活性。這種靈活性體現在其他的方式。你可以讓你的遊戲基於事件,或檢查事件和回應他們按順序更新功能。例如,若要處理一項關鍵投入,你可以對事件作出回應:

game.input.keyboard.onDownCallback = function(key) {
  console.log(key + " was pressed.");
}

或檢查是否按下了鍵,尤其是在您的更新迴圈:

function update() {
  if (game.input.keyboard.isDown('a'.charCodeAt(0)) {
    console.log("'A' key was pressed");
  }
}

這是如此大量的相位,使用順序或非同步編碼根據您的喜好或需要的事件。有時,相移器提供事件處理的只是後者的形式。是一個好例子了拱廊街物理系統,在那裡您必須明確指定函式呼叫以檢查有物體之間的碰撞的每次更新:

game.physics.arcade.collide(player, ball, function() {
  ballOwner = player;
});

這段代碼確定是否球曾接觸過的球員雪碧,並且給玩家控制球,當發生這種情況。查閱在相位平與本文的代碼下載我執行。

接近尾聲了

如果您正在尋找一個專用的 2D JavaScript 遊戲引擎,有很多選擇,以滿足您的需求。下面是兩個重要的因素來選擇一個框架時,請牢記:

使用只有您需要的功能:Don不被眼花繚亂的大量特徵、 包羅萬象的整體框架和其他的鐘聲和口哨聲。除非鐘聲和口哨聲解決具體方面你想要的遊戲,否則他們可能會的方式。此外考慮如何被元件化,功能。如果您刪除物理系統,Will整個系統仍工作嗎?該框架依賴于物理系統,以提供其他功能嗎?

瞭解每個引擎的工作原理:試著瞭解如何您可以自訂或擴展框架。如果你犯了一些略有不同尋常的東西,很可能需要編寫自訂代碼依賴于你所選擇的框架。在這一點上,你可以編寫一個新的、 獨立的功能或擴展框架功能。如果您擴展框架本身而不是添加到它,你會得到更多可維護的代碼。

這裡介紹的內容框架解決許多這類的問題,所以他們肯定是值得一看。有很多的引擎、 庫和框架在那裡,所以您下次的遊戲開發冒險之前做一些研究。


Michael Oneppo *是一個創造性的技師和 Direct3D 團隊在微軟前程式經理。他最近的努力包括在這項技術作為首席技術官工作非營利組織所有圖書館和探索在紐約大學電訊互動專案碩士學位。*​

感謝以下 Microsoft 技術專家,檢討這篇文章:札斯廷 · 加勒特