pixi.js是一個很是快速的2D精靈渲染引擎。它能夠幫助咱們顯示,動畫和管理交互式圖形。如此一來,咱們可使用javascript和其它HTML5技術來輕鬆實現一個應用程序或者完成一款遊戲。它有一個語義化的、簡潔的API,包含了許多有用的功能。好比說支持紋理地圖集,也提供一個經過動畫精靈(交互式圖像)所構建的精簡的系統。它還爲咱們提供了一個完整的場景圖,咱們能夠建立嵌套精靈的層次結構(也就是精靈當中嵌套精靈)。一樣的也容許咱們將鼠標以及觸摸的事件添加到精靈上。並且,最重要的仍是,pixi.js能夠根據咱們的實際需求來使用,很好的適應我的的編碼風格,而且還可以和框架無縫集成。javascript
pixi.js的API事實上是陳舊的Macromedia/Adobe Flash API的一個改進。熟練flash的開發人員將會有一種回到本身家裏同樣的熟悉。其它使用過相似API的精靈渲染框架有: CreateJS,Starling, Sparrow 和 Apple’s SpriteKit。pixi.js的優勢在於它的通用性:它不是一個遊戲引擎。這是極好的,由於它能夠徹底任由咱們自由發揮,作本身的事情,甚至還能夠用它寫一個本身的遊戲引擎。在學習pixi.js以前,咱們須要先對HTML,CSS,Javascript有一些瞭解。由於這會對咱們學習pixi.js有很大的幫助。css
能夠前往github上安裝,在這裏。也能夠在這裏下載安裝。html
也可使用npm來安裝。首先須要安裝node.js。 當安裝node.js完成以後,會自動完成npm的安裝。而後就能夠經過npm將pixi.js安裝在全局。命令以下:html5
npm install pixi.js -g
//也能夠簡寫爲
npm i pixi.js -g
複製代碼
或者也能夠將pixi.js安裝在當前項目的目錄之下。命令以下:java
npm install pixi.js -D //或者
npm install pixi.js --save -dev
//也能夠簡寫爲
npm i pixi.js -D
複製代碼
安裝pixi.js完成以後,咱們就可使用了。首先在你的項目目錄下建立一個基礎的.html文件。 而後在你html文件中,使用script標籤來,加載這個js文件。代碼以下:node
<script src="/pixi.min.js"></script>
複製代碼
或者,你也可使用CDN來引入這個js文件。以下:git
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
<!--或者使用bootstrap的cdn來引入-->
<script src="https://cdn.bootcss.com/pixi.js/5.1.3/pixi.min.js"></script>
複製代碼
若是使用es6的模塊化加載pixi.js,那麼就須要注意了,由於pixi.js沒有默認導出。因此正確的引入方式應該以下:es6
import * as PIXI from 'pixi.js'
複製代碼
好了,接下來就是寫點js代碼,看看pixi.js是否在工做中。代碼以下:github
const type = "WebGL";
if (!PIXI.utils.isWebGLSupported()) {
type = "canvas";
}
PIXI.utils.sayHello(type);
複製代碼
若是能在瀏覽器控制檯中,看到以下圖所示的標誌,那麼就證實pixi.js加載成功。web
咱們來分析一下以上的代碼的意思,先定義一個變量,值是字符串"webGL",而後有一個if語句判斷若是支持webGL,那麼就改變這個變量的值爲canvas。而後調用pixi.js所封裝的在控制檯打印這個值的方法,即sayHello方法。
如今,咱們能夠愉快的開始使用pixi.js呢。首先,咱們能夠建立一個能夠顯示圖片的矩形區域,pixi.js有一個Application對象來幫助咱們建立它。它會自動建立一個canvas的HTML標籤。而且還可以自動計算出讓你的圖片可以在canvas元素中顯示。而後,你須要建立一個特殊的pixi容器對象,它被叫作舞臺。正如你所看到的,這個舞臺元素會被當作根容器,而後你能夠在這個根容器使用pixi來顯示你想要顯示的東西。如下是你須要建立一個pixi應用對象以及舞臺對象所必要的代碼。
//建立一個pixi應用對象
let app = new PIXI.Application({width: 256, height: 256});
//將這個應用對象元素添加到dom文檔中
document.body.appendChild(app.view);
複製代碼
以上代碼運行在瀏覽器中,效果如圖所示:
很好,以上的代碼所表明的意思就是,咱們在HTML DOM中建立了背景顏色爲黑色(默認顏色)的一個寬爲256,高爲256的canvas元素(單位默認是像素)。沒錯,就是一個黑色的矩形。咱們能夠看到PIXI.Application是一個構造函數,它會根據瀏覽器是支持canvas仍是webGL來決定使用哪個渲染圖像。函數裏面能夠不傳參數,也能夠傳一個對象當作參數。若是不傳參數,那麼會使用默認的參數。這個對象,咱們能夠稱之爲option對象。好比以上,咱們就傳了width和height屬性。
固然,咱們還可使用更多的屬性,例如如下代碼:
let app = new PIXI.Application({
width: 256, //default:800
height: 256, //default:600
antialias: true, //default:false
transparent: false, //default:false
resolution: 1 //default:1
});
複製代碼
這些屬性到底表明什麼意思呢?antialias屬性使得字體的邊界和圖形更加平滑(webGL的anti-aliasing在全部平臺都不可用,你須要在你的遊戲平臺中去作測試。)。transparent屬性是設置整個canvas元素的透明度。resolution屬性讓不一樣分辨率和不一樣像素的平臺運行起來更容易一些。只要將這個屬性的值設置爲1就能夠應付大多數的工程項目呢。想要知道這個屬性的全部細節,能夠查看這個項目Mat Grove'sexplanation的代碼。
pixi.js默認是經過WebGL來渲染的。由於webGL的速度很是塊,而且咱們還可使用一些將要學習的壯觀的視覺效果。固然,若是須要強制性的使用Canvas來取代webGL渲染。咱們能夠將forceCanvas的值設置爲true,便可。以下:
forceCanvas:true
複製代碼
在你建立了canvas元素以後,若是你想要改變它的背景顏色,你須要設置app.renderer.backgroundColor
屬性爲任意的十六進制顏色值("0X"加上"0"~"f"之間的任意6個字符組成的8位字符。)。例如:
app.renderer.backgroundColor = 0x061639;
複製代碼
若是想要獲取canvas的寬和高,可使用app.renderer.view.width和app.renderer.view.height屬性。
固然,咱們也能夠改變canvas的大小,只須要使用renderer.resize方法而且傳入width和height屬性的值便可。不過,爲了確保大小可以正確的適應平臺的分辨率,咱們須要將autoResize的值設置爲true。以下:
app.renderer.autoResize = true;
app.renderer.resize(512, 512);//第一個512表明寬度,第二個512表明高度
複製代碼
若是想要canvas填滿整個瀏覽器的窗口,能夠提供css樣式,而後將canvas的大小設置爲瀏覽器的窗口大小。以下:
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
複製代碼
可是,若是這樣作了以後,確保使用以下的css代碼來讓全部HTML元素的margin和padding初始化爲0。
* {
margin:0;
padding:0;
}
複製代碼
上面代碼中的星號*是CSS「通用選擇器」,它的意思是「HTML文檔中的全部標記」。
若是但願讓canvas按比例縮放到任何瀏覽器窗口大小,則可使用自定義的scaleToWindow函數。能夠點擊這裏查看更多應用對象配置屬性。
如今,咱們已經有了一個渲染器,或者咱們能夠稱之爲畫布。咱們能夠開始往畫布上面添加圖片。咱們但願顯示的任何內容都必須被添加到一個叫作stage(舞臺)的特殊pixi對象中。咱們能夠像以下那樣使用這個舞臺:
//app爲實例化的應用對象
let stage = app.stage;//如今這個stage變量就是舞臺對象
複製代碼
stage是一個特殊的pixi容器對象。咱們能夠將它看做是一個空盒子,而後和咱們添加進去的任何內容組合在一塊兒,而且存儲咱們添加的任何內容。stage對象是場景中全部可見對象的根容器。不管咱們在舞臺中放入什麼內容都會在畫布中呈現。如今咱們的這個盒子仍是空的,沒有什麼內容,可是咱們很快就會放點內容進去。能夠點擊這裏查看關於pixi容器對象的更多信息。
重要說明: 由於stage是一個pixi容器。因此它擁有有和其它任何容器同樣的屬性方法。雖然stage擁有width和height屬性,可是它們並不指的是渲染窗口的大小。stage的width和height屬性僅僅是爲了告訴咱們放在裏面的東西所佔據的區域——更多關於它的信息在前面。
那麼咱們應該在舞臺上面放些什麼東西呢?就是一些被稱做爲精靈的特殊圖片對象。精靈基本上就是咱們能用代碼控制的圖片。咱們能夠控制它們的位置,大小,以及其它用於動畫和交互的有用的屬性。學會如何製做與控制一個精靈真的是一件關於學習如何使用pixi.js的重要的事情。若是咱們知道如何製做精靈而且可以把它們添加到舞臺中,咱們距離製做遊戲僅剩一步之遙。
pixi有一個精靈類,它是一種製做遊戲精靈的多功能的方式。有三種主要的方式來建立它們:
1.從單個圖片文件中建立。
2.用一個 雪碧圖來建立。雪碧圖是一個放入了你遊戲所需的全部圖像的大圖。
3.從紋理圖集(一個JSON文件,在雪碧圖中定義圖片大小和位置)。
複製代碼
咱們將學習這三種方式。但在此以前,咱們先了解一下咱們須要瞭解的圖片,而後使用pixi顯示這些圖片。
因爲pixi使用webGL在GPU上渲染圖片,因此圖片須要採用GPU可以處理的格式。使用webGL渲染的圖像就被叫作紋理。在你讓精靈顯示圖片以前,須要將普通的圖片轉化成WebGL紋理。爲了讓全部東西在幕後快速有效地工做,Pixi使用紋理緩存來存儲和引用精靈所需的全部紋理。紋理的名稱是與它們引用的圖像的文件位置匹配的字符串。 這意味着若是你有一個從"images/cat.png"加載的紋理,你能夠在紋理緩存中找到它,以下所示:
PIXI.utils.TextureCache["images/cat.png"];
複製代碼
紋理以WebGL兼容格式存儲,這對於Pixi的渲染器來講很是有效。而後,您可使用Pixi的Sprite類使用紋理製做新的精靈。
let texture = PIXI.utils.TextureCache["images/anySpriteImage.png"];
let sprite = new PIXI.Sprite(texture);
複製代碼
可是如何加載圖像文件並將其轉換爲紋理?使用Pixi的內置loader對象。Pixi強大的loader對象是加載任何類型圖片所需的所有內容。如下是如何使用它來加載圖像並在圖像加載完成後調用一個名爲setup的函數:
PIXI.loader.add("images/anyImage.png").load(setup);
function setup() {
//此代碼將在加載程序加載完圖像時運行
}
複製代碼
若是使用loader來加載圖像,這是pixi開發團隊的建議。咱們應該經過引入loader's resources對象中的圖片資源來建立精靈,就像以下這樣:
let sprite = new PIXI.Sprite(
//數組內是多張圖片資源路徑
PIXI.loader.resources["images/anyImage.png"].texture
);
複製代碼
如下是一個完整的代碼的例子,咱們能夠編寫這些代碼來加載圖像,調用setup函數,而後從加載的圖像中建立精靈。
PIXI.loader.add("images/anyImage.png").load(setup);
function setup() {
let sprite = new PIXI.Sprite(
PIXI.loader.resources["images/anyImage.png"].texture
);
}
複製代碼
這是加載圖像和建立圖片的通用格式。咱們還可使用鏈式調用的add方法來同時加載多個圖像,以下:
PIXI.loader.add("images/imageOne.png").add("images/imageTwo.png").add("images/imageThree.png").load(setup);
複製代碼
固然,更好的方式是,只需在單個add方法內將要加載的全部文件列出在數組中,以下所示:
PIXI.loader.add(["images/imageOne.png","images/imageTwo.png","images/imageThree.png"]).load(setup);
複製代碼
固然加載程序還容許咱們加載JSON文件。這在後面,咱們會學到。
在咱們已經加載了圖片,而且將圖片製做成了精靈,咱們須要使用stage.addChild方法來將精靈添加到pixi的stage(舞臺)中。就像以下這樣:
//cat表示精靈變量
app.stage.addChild(cat);
複製代碼
注: 記住stage(舞臺)是全部被添加的精靈的主容器元素。並且咱們不會看到任何咱們所構建的精靈,除非咱們已經把它們添加到了stage(舞臺)中。
好的,讓咱們來看一個示例關於如何使用代碼來學會在舞臺上顯示一張單圖圖像。咱們假定在目錄examples/images下,你會發現有一張寬64px高64px的貓圖。以下所示:
如下是一個須要加載圖片,建立一個精靈,而且顯示在pixi的stage上的全部JavaScript代碼:
//建立一個pixi應用對象
let app = new PIXI.Application({
width: 256,
height: 256,
antialias: true,
transparent: false,
resolution: 1
}
);
//將canvas元素添加到body元素中
document.body.appendChild(app.view);
//加載圖像,而後使用setup方法運行
//"images/cat.png"這個路徑須要根據本身狀況所調整
PIXI.loader.add("images/cat.png").load(setup);
//當圖片加載完成,setup方法會執行
function setup() {
//建立cat精靈,"images/cat.png"這個路徑須要根據本身狀況所調整
let cat = new PIXI.Sprite(PIXI.loader.resources["images/cat.png"].texture);
//將cat精靈添加到舞臺中
app.stage.addChild(cat);
}
複製代碼
當代碼運行在瀏覽器上,你會看到如圖所示:
在線示例。
若是咱們須要從舞臺上移除精靈,咱們可使用removeChild方法。以下:
//參數爲精靈圖的路徑
app.stage.removeChild(anySprite)
複製代碼
可是一般將精靈的visible屬性設置爲false將是使精靈消失的更簡單,更有效的方法。以下:
//anySprite爲精靈對象,例如前面示例的cat
anySprite.visible = false;
複製代碼
固然咱們也能夠對咱們使用頻繁的pixi對象和方法建立一些簡略的可讀性更好的別名。例如,你難道想給全部的pixi對象添加PIXI前綴嗎?若是不這樣想,那就給它一個簡短的別名吧。例如:如下是爲TextureCache對象所建立的一個別名。
let TextureCache = PIXI.utils.TextureCache;
複製代碼
而後,使用該別名代替原始別名,以下所示:
//"images/cat.png"這個路徑須要根據本身狀況所調整
let texture = TextureCache["images/cat.png"];
複製代碼
使用別名給寫出簡潔的代碼提供了額外的好處:他幫助你緩存了Pixi的經常使用API。若是Pixi的API在未來的版本里改變了-沒準他真的會變!你將會須要在一個地方更新這些對象和方法,你只用在工程的開頭而不是全部的實例那裏!因此Pixi的開發團隊想要改變它的時候,你只用一步便可完成這個操做! 來看看怎麼將全部的Pixi對象和方法改爲別名以後,來重寫加載和顯示圖像的代碼。
//別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
//建立一個應用對象
let app = new Application({
width: 256,
height: 256,
antialias: true,
transparent: false,
resolution: 1
}
);
//將Pixi自動爲您建立的畫布添加到HTML文檔中
document.body.appendChild(app.view);
//加載圖像並完成後運行「setup」函數
loader.add("images/cat.png").load(setup);
//該「setup」函數將在圖像加載後運行
function setup() {
//建立一個cat精靈類
let cat = new Sprite(resources["images/cat.png"].texture);
//將cat精靈類添加到舞臺中
app.stage.addChild(cat);
}
複製代碼
大多數教程中的例子將會使用Pixi的別名來處理。除非另有說明,不然你能夠假定下面全部的代碼都使用了這些別名。這就是咱們所須要知道的全部的關於加載圖像和建立精靈的知識。
前面所顯示的格式是建議用做加載圖像和顯示圖片的標準模板的格式。 所以,咱們能夠放心地忽略接下來的幾段內容,而直接跳到下一部分「定位精靈」。 可是Pixi的加載程序對象很是複雜,即便您不按期使用它們,也要注意一些功能。 讓咱們看一些最有用的。
爲了優化和提升效率,始終最好從預先加載到Pixi的紋理緩存中的紋理製做精靈。 可是,若是因爲某種緣由須要從常規的JavaScript Image對象製做紋理,則可使用Pixi的BaseTexture和Texture類來實現:
//參數爲任何JavaScriptImage對象
let base = new PIXI.BaseTexture(anyImageObject);
let texture = new PIXI.Texture(base);
let sprite = new PIXI.Sprite(texture);
複製代碼
若是要從任何現有的canvas元素製做紋理,可使用BaseTexture.fromCanvas:
//參數爲任何canvas元素
let base = new PIXI.BaseTexture.fromCanvas(anyCanvasElement);
複製代碼
若是要更改精靈顯示的紋理,請使用texture屬性。 將其設置爲任何texture對象,以下所示:
anySprite.texture = PIXI.utils.TextureCache["anyTexture.png"];
複製代碼
若是遊戲中發生重大變化,就可使用此技術交互式地更改精靈的外觀。
能夠爲要加載的每一個資源分配一個惟一的名稱。只需提供名稱(字符串)做爲add方法中的第一個參數便可。 例如,如下是將cat的圖像命名爲catImage的方法。
//第一個參數爲分配的別名,第二個參數則是圖像路徑
PIXI.loader.add("catImage", "images/cat.png").load(setup);
複製代碼
這將在loader.resources中建立一個名爲catImage的對象。 這意味着能夠經過引用catImage對象來建立一個精靈,以下所示:
//catImage對象下的texture屬性
let cat = new PIXI.Sprite(PIXI.loader.resources.catImage.texture);
複製代碼
可是,建議不要使用此功能! 這是由於使用它就必須記住爲每一個已加載文件指定的全部名稱,並確保不要意外地屢次使用同一名稱。正如在前面的示例中所作的那樣,使用文件路徑名更加簡單,而且不容易出錯。
Pixi的加載程序有一個特殊的progress事件,它將調用一個可自定義的函數,該函數將在每次文件加載時運行。進度事件由加載器的on方法調用,以下所示:
//loadProgressHandler爲處理進度的函數
PIXI.loader.on("progress", loadProgressHandler);
複製代碼
如下爲在加載鏈中使用包括on方法的方式,並在每次文件加載時調用用戶定義的函數loadProgressHandler。
//使用on方法
PIXI.loader.add([
"images/one.png",
"images/two.png",
"images/three.png"
]).on("progress", loadProgressHandler).load(setup);
//loadProgressHandler函數
function loadProgressHandler() {
console.log("loading");
}
//setup函數
function setup() {
console.log("setup");
}
複製代碼
每次加載其中一個文件時,進度事件都會調用loadProgressHandler以在控制檯中顯示「loading」。當全部三個文件都加載完畢後,setup函數將運行。 如下是上述代碼在控制檯中的輸出:
loading
loading
loading
setup
複製代碼
這很不錯了,可是會變得更好。咱們還能夠準確地找到已加載的文件以及當前已加載的文件總數的百分比。只須要經過向loadProgressHandler添加可選的loader和resource參數來作到這一點,以下所示:
function loadProgressHandler(loader, resource) {
//從resouce中取得已加載的文件或者取得已加載文件的百分比
}
複製代碼
而後,可使用resource.url查找當前加載的文件。(若是要查找可能已分配給文件的可選名稱,請使用resource.name做爲add方法中的第一個參數。)而後,您可使用loader.progress查找當前已加載的總資源百分比。如下是一些執行此操做的代碼。
PIXI.loader.add([
"images/one.png",
"images/two.png",
"images/three.png"
]).on("progress", loadProgressHandler).load(setup);
function loadProgressHandler(loader, resource) {
//顯示當前加載的文件路徑
console.log("loading: " + resource.url);
//顯示當前文件加載的百分比
console.log("progress: " + loader.progress + "%");
//若是第一個參數提供的是文件的可選名稱
//那麼在add方法裏就要像以下這樣接收它們
//console.log("loading:"+resource.name);
}
function setup() {
console.log("All files loaded");
}
複製代碼
如下是此代碼在運行時將在控制檯中顯示的內容:
loading: images/one.png
progress: 33.333333333333336%
loading: images/two.png
progress: 66.66666666666667%
loading: images/three.png
progress: 100%
All files loaded
複製代碼
這確實好棒,由於咱們能夠以此爲基礎建立加載進度條。
注意: 咱們也能夠在資源對象上訪問其餘屬性。resource.error會告訴您嘗試加載文件時發生的任何可能的錯誤。resource.data容許您訪問文件的原始二進制數據。
Pixi的loader具備豐富的功能和可配置性。讓咱們快速瞭解一下它的用法,好入門。 loader的可連接的add方法包含4個基本參數:
add(name, url, optionObject, callbackFunction);
複製代碼
如下是對這些參數作描述的簡單文檔:
1.name (string):要加載的資源的名稱。若是未被使用,則會自動使用url。
2.url (string):此資源的網址,相對於loader的baseUrl。
3.options (object literal):加載的選項。
4.options.crossOrigin (Boolean):請求是跨域的嗎? 默認爲自動肯定。
5.options.loadType:資源應如何加載? 默認值爲Resource.LOAD_TYPE.XHR。
6.options.xhrType:使用XHR時應如何執行正在加載的數據?默認值爲Resource.XHR_RESPONSE_TYPE.DEFAULT。
7.callbackFunction:當資源完成加載時所要調用的函數(回調函數)。
複製代碼
這些參數中惟一須要傳入的就是url(要加載的文件)。如下是一些可使用add方法加載文件的方式的示例。 這是文檔稱爲loader的「常規語法」:
//第一個參數爲加載資源的名稱,第二個參數爲資源路徑,而後第三個參數可不傳,也就是加載的選項,第四個參數就是回調函數
PIXI.loader.add('key', 'http://...', function () {});
PIXI.loader.add('http://...', function () {});
PIXI.loader.add('http://...');
複製代碼
如下這些是loader的「對象語法」的示例:
PIXI.loader.add({
name: 'key2',
url: 'http://...'
}, function () {})
PIXI.loader.add({
url: 'http://...'
}, function () {})
PIXI.loader.add({
name: 'key3',
url: 'http://...'
onComplete: function () {}
})
PIXI.loader.add({
url: 'https://...',
onComplete: function () {},
crossOrigin: true
})
複製代碼
您還能夠將對象或URL或二者的數組傳遞給add方法:
PIXI.loader.add([
{name: 'key4', url: 'http://...', onComplete: function () {} },
{url: 'http://...', onComplete: function () {} },
'http://...'
]);
複製代碼
注意: 若是須要重置loader以加載新一批文件,請調用loader的reset方法:PIXI.loader.reset()。
Pixi的loader具備許多更高級的功能,包括使咱們能夠加載和解析全部類型的二進制文件的選項。這不是咱們平常要作的事情,而且超出了目前咱們所學習的範圍,所以能夠從GitHub項目中獲取更多信息。
如今咱們知道了如何建立和顯示精靈,讓咱們瞭解如何放置和調整精靈的大小。在前面的示例中,cat sprite已添加到舞臺的左上角。cat的x位置爲0,y位置爲0。能夠經過更改cat的x和y屬性的值來更改cat的位置。經過將cat的x和y屬性值設置爲96的方法,可使cat在舞臺中居中。
cat.x = 96;
cat.y = 96;
複製代碼
建立精靈後,將以上兩行代碼添加到setup函數內的任何位置。
function setup() {
//建立cat精靈
let cat = new Sprite(resources["images/cat.png"].texture);
//改變精靈的位置
cat.x = 96;
cat.y = 96;
//將cat精靈添加到舞臺中如此即可以看到它
app.stage.addChild(cat);
}
複製代碼
注意: 在這個例子中,Sprite是PIXI的別名。Sprite,TextureCache是PIXI.utils.TextureCache的別名,resources是PIXI.loader.resources的別名。後面都是使用別名,而且從如今開始,示例代碼中全部Pixi對象和方法的格式都相同。
這兩行代碼會將cat右移96像素,向下移96像素。結果以下:
在線示例。
cat的左上角(左耳)表明其x和y錨點。要使cat向右移動,請增長其x屬性的值。要使cat向下移動,請增長其y屬性的值。若是cat的x值爲0,則它將位於舞臺的最左側。若是y值爲0,則它將位於該階段的頂部。以下圖所示:
其實能夠沒必要單獨設置精靈的x和y屬性,而是能夠在一行代碼中將它們一塊兒設置,以下所示:
//也就是調用set方法便可,傳入修改的x參數和y參數
sprite.position.set(x, y)
複製代碼
讓咱們來看看以上的示例代碼修改以後的結果:
在線示例。
能夠看出來,結果都是同樣的。
咱們能夠經過設置精靈的width和height屬性來更改其大小。如下即是一個示例,將cat設置爲80像素的寬度和120像素的高度。
cat.width = 80;
cat.height = 120;
複製代碼
將這兩行代碼添加到setup函數中,就像以下:
function setup() {
//建立cat精靈
let cat = new Sprite(resources["images/cat.png"].texture);
//改變精靈的位置
cat.x = 96;
cat.y = 96;
//改變精靈的大小
cat.width = 80;
cat.height = 120;
//將cat精靈添加到舞臺中如此即可以看到它
app.stage.addChild(cat);
}
複製代碼
效果如圖所示:
在線示例。
咱們會看到cat的位置(左上角)沒有變化,只是寬度和高度有變化。以下圖所示:
精靈還具備scale.x和scale.y屬性,可按比例更改精靈的寬度和高度。如下是將cat的scale設置爲一半尺寸的方法:
cat.scale.x = 0.5;
cat.scale.y = 0.5;
複製代碼
scale是介於0和1之間的數字,表明精靈大小的百分比。1表示100%(原尺寸),而0.5表示50%(半尺寸)。您能夠經過將精靈的scale設置爲2來使精靈大小增長一倍,以下所示:
cat.scale.x = 2;
cat.scale.y = 2;
複製代碼
Pixi提供了另外一種簡潔的方法,您可使用scale.set方法在一行代碼中設置精靈的縮放比例。
//注意參數表明的意思
cat.scale.set(0.5, 0.5);
複製代碼
若是喜歡這樣使用,那就這樣用吧!咱們來看一個完整的示例:
//別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
//建立一個應用對象
let app = new Application({
width: 256,
height: 256,
antialias: true,
transparent: false,
resolution: 1
});
//將Pixi自動爲您建立的畫布添加到HTML文檔中
document.body.appendChild(app.view);
//加載圖像並完成後運行「setup」函數
loader.add("/static/page/PIXIJS/images/cat.png").load(setup);
//該「setup」函數將在圖像加載後運行
function setup() {
//建立cat精靈
let cat = new Sprite(resources["/static/page/PIXIJS/images/cat.png"].texture);
//改變精靈的位置
cat.position.set(96, 96);
//改變精靈的大小
cat.scale.set(0.5,0.5);
//或者這樣使用
//cat.scale.x=0.5
//cat.scale.y=0.5
//將cat精靈添加到舞臺中如此即可以看到它
app.stage.addChild(cat);
}
複製代碼
運行效果如圖所示:
在線示例。
咱們也能夠經過將精靈的rotation屬性設置爲以弧度爲單位的值來使其旋轉。以下所示:
cat.rotation = 0.5;
複製代碼
可是旋轉發生在哪一點附近?咱們能夠從下圖中看到精靈的左上角表明其x和y位置。該點稱爲錨點。 若是將精靈的rotation屬性設置爲0.5,則旋轉將圍繞精靈的錨點進行。咱們也會知道這將對咱們的cat精靈產生什麼影響。
咱們會看到錨點,即cat的左耳,是cat圍繞其旋轉的假想圓的中心。 若是要讓精靈圍繞其中心旋轉怎麼辦?更改精靈的錨點,使其居中,以下所示://anchor就是錨點
cat.anchor.x = 0.5;
cat.anchor.y = 0.5;
複製代碼
anchor.x和anchor.y值表明紋理尺寸的百分比,範圍爲0到1(0%到100%)。將其設置爲0.5可以使紋理在該點上居中。點自己的位置不會改變,只是紋理在其上定位的方式同樣。下一張圖顯示了若是將居中的錨點居中,旋轉的精靈會發生什麼。
咱們會看到精靈的紋理向上和向左移動。這是要記住的重要反作用!就像position和scale同樣,咱們也可使用如下一行代碼來設置錨點的x和y值:
//注意參數便可
cat.anchor.set(x, y);
複製代碼
精靈還具備pivot屬性,其做用方式相似於anchor。 pivot設置精靈的x / y原點的位置。 若是更改軸心點而後旋轉精靈,它將圍繞該原點旋轉。例如,下面的代碼將把精靈的pivot.x指向32,將其pivot.y指向32。
//注意參數的意義
cat.pivot.set(32, 32);
複製代碼
假設精靈爲64x64像素,則精靈如今將圍繞其中心點旋轉。可是請記住:若是更改了精靈的pivot,則還更改了它的x / y原點。那麼,anchor和pivot點有什麼區別?他們真的很類似!anchor使用0到1歸一化的值移動精靈圖像紋理的原點。pivot使用像素值移動精靈的x和y的原點。咱們應該使用哪一個?由咱們本身決定。喜歡用哪一個就用哪一個便可。讓咱們來看看使用這兩個屬性的完整示例吧!
如今,咱們也知道了如何從單個圖像文件製做精靈。可是,做爲遊戲設計師,一般會使用雪碧圖(也稱爲精靈圖)來製做精靈。Pixi具備一些方便的內置方法來幫助咱們完成此任務。所謂的雪碧圖就是包含子圖像的單個圖像文件。子圖像表明要在遊戲中使用的全部圖形。如下是圖塊圖像的示例,其中包含遊戲角色和遊戲對象做爲子圖像。
整個雪碧圖爲192 x 192像素。每一個圖像都位於其本身的32 x 32像素網格單元中。在圖塊上存儲和訪問全部遊戲圖形是一種處理圖形的很是高效的處理器和內存方式,Pixi爲此進行了優化。 咱們能夠經過定義與咱們要提取的子圖像大小和位置相同的矩形區域,來從雪碧圖中捕獲子圖像。如下是從雪碧圖中提取的火箭子圖像的示例。
讓咱們看看執行此操做的代碼。首先,就像在前面的示例中所作的那樣,使用Pixi的loader加載tileset.png圖像。
//注意這裏的路徑依據實際狀況來修改調整
loader.add("images/tileset.png").load(setup);
複製代碼
接下來,在加載圖像後,使用雪碧圖的矩形塊來建立精靈的圖像。如下是提取子圖像,建立火箭精靈並將其定位並顯示在畫布上的代碼。
function setup() {
//從紋理建立「tileset」精靈
let texture = TextureCache["images/tileset.png"];
//建立一個定義位置矩形對象
//而且要從紋理中提取的子圖像的大小
//`Rectangle`是`PIXI.Rectangle`的別名,注意這裏的參數,後續會詳解,參數值與實際狀況有關
let rectangle = new Rectangle(192, 128, 64, 64);
//告訴紋理使用該矩形塊
texture.frame = rectangle;
//從紋理中建立一個精靈
let rocket = new Sprite(texture);
//定位火箭精靈在canvas畫布上
rocket.x = 32;
rocket.y = 32;
//將火箭精靈添加到舞臺中
app.stage.addChild(rocket);
//從新渲染舞臺
app.renderer.render(app.stage);
}
複製代碼
這是如何工做的?Pixi具備內置的Rectangle對象(PIXI.Rectangle),它是用於定義矩形形狀的通用對象。它有四個參數。前兩個參數定義矩形的x和y位置。最後兩個定義其寬度和高度。這是定義新Rectangle對象的格式。
let rectangle = new PIXI.Rectangle(x, y, width, height);
複製代碼
矩形對象只是一個數據對象。由咱們本身來決定如何使用它。在咱們的示例中,咱們使用它來定義要提取的圖塊上的子圖像的位置和區域。Pixi紋理具備一個有用的屬性,稱爲frame,能夠將其設置爲任何Rectangle對象。frame將紋理裁剪爲矩形的尺寸。如下是使用frame將紋理裁剪爲火箭的大小和位置的方法。
let rectangle = new Rectangle(192, 128, 64, 64);
texture.frame = rectangle;
複製代碼
而後,咱們就可使用該裁剪的紋理來建立精靈:
let rocket = new Sprite(texture);
複製代碼
而後它就開始運行啦。因爲咱們會頻繁地使用雪碧圖製做精靈紋理,所以Pixi提供了一種更方便的方法來幫助咱們完成此任務-讓咱們繼續下一步。
在線示例。
若是咱們是開發大型複雜的遊戲,則須要一種快速有效的方法來從雪碧圖建立精靈。這是紋理圖集真正有用的地方。紋理圖集是JSON數據文件,其中包含匹配的圖塊PNG圖像上子圖像的位置和大小。若是使用紋理圖集,那麼關於要顯示的子圖像,咱們所須要知道的就是它的名稱。咱們能夠按任何順序排列雪碧圖圖像,JSON文件將爲咱們跟蹤其大小和位置。這真的很方便,由於這意味雪碧圖圖片的大小和位置不會硬編碼到咱們的遊戲程序中。若是咱們對雪碧圖進行更改(例如添加圖像,調整圖像大小或將其刪除),則只需從新發布JSON文件,咱們的遊戲就會使用該數據顯示正確的圖像。咱們無需對遊戲代碼進行任何更改。
Pixi與一種流行的名爲Texture Packer的軟件工具輸出的標準JSON紋理圖集格式兼容。Texture Packer的「基本」許可證是免費的。讓咱們瞭解如何使用它製做紋理圖集,並將該圖集加載到Pixi中。(咱們也能夠沒必要使用Texture Packer。相似的工具(例如Shoebox或spritesheet.js)能夠以與Pixi兼容的標準格式輸出PNG和JSON文件。)
首先,要收集在遊戲中使用的單個圖像文件。
注: (本文中的全部圖像均由Lanea Zimmerman建立。您能夠在此處找到她的更多做品。謝謝Lanea Zimmerman!)
接下來,打開Texture Packer,而後選擇JSON Hash做爲框架類型。將圖像拖到Texture Packer的工做區中。(咱們也能夠將Texture Packer指向包含圖像的任何文件夾。)它將自動將圖像排列在單個雪碧圖上,併爲其提供與原始圖像名稱匹配的名稱。
注:(若是使用的是免費版本的Texture Packer,則將Algorithm設置爲Basic,將Trim mode模式設置爲None,將Extrude設置爲0,將Size constraints 設置爲Any size,而後將PNG Opt Level一直滑到左邊至0。這些是基本設置,可以讓免費版本的Texture Packer建立文件而沒有任何警告或錯誤。)
完成後,點擊Publish按鈕。選擇文件名和存儲位置,而後保存發佈的文件。 最終將得到2個文件:一個PNG文件和一個JSON文件。 在此示例中,文件名是treasureHunter.json和treasureHunter.png。爲了簡便點,只需將兩個文件都保存在一個名爲images的文件夾中。(能夠將JSON文件視爲圖像文件的額外元數據,所以將兩個文件都保留在同一文件夾中是頗有意義的。)JSON文件描述了圖集中每一個子圖像的名稱,大小和位置。如如下摘錄了一個文件內容,描述了Blob Monster(泡泡怪)子圖像。
"blob.png":
{
"frame": {"x":55,"y":2,"w":32,"h":24},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":32,"h":24},
"sourceSize": {"w":32,"h":24},
"pivot": {"x":0.5,"y":0.5}
},
複製代碼
treasureHunter.json文件還包含「dungeon.png」,「door.png」,「exit.png」和「explorer.png」屬性,每一個屬性都具備類似的數據。這些子圖像中的每個都稱爲幀。擁有這些數據確實有幫助,由於如今無需知道紋理圖集中每一個子圖像的大小和位置。只須要知道精靈的幀ID。幀ID只是原始圖像文件的名稱,例如「blob.png」或「explorer.png」。
使用紋理圖集的衆多優勢之一是,能夠輕鬆地在每一個圖像周圍添加2個像素的填充(默認狀況下,Texture Packer會這樣作。)這對於防止紋理滲漏的可能性很重要。紋理出血(注:出血是排版和圖片處理方面的專有名詞,指在主要內容周圍留空以便印刷或裁切)是當圖塊上相鄰圖像的邊緣出如今精靈旁邊時發生的一種效果。發生這種狀況的緣由是計算機的GPU(圖形處理單元)決定如何舍入小數像素值的方式。它應該向上或向下取整?每一個GPU都不一樣。在GPU上的圖像周圍留出1或2個像素的間距,可以使全部圖像始終顯示一致。
注:(若是圖形周圍有兩個像素填充,而且在Pixi的顯示方式中仍然發現奇怪的「偏離一個像素」故障,請嘗試更改紋理的縮放模式算法。方法以下:
texture.baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;
複製代碼
。因爲GPU浮點舍入錯誤,有時會發生這些故障。)
如今咱們已經知道如何建立紋理圖集,讓咱們瞭解如何將其加載到遊戲代碼中。
ps:關於以上的示例所涉及到的圖片資源可點擊此處下載。
下圖爲本人使用Texture Packer建立的紋理圖集的一個展現:
可點擊此處(JSON), 此處(png)下載已經建立的紋理圖集JSON文件和PNG文件。
可使用Pixi的loader來加載紋理貼圖集。若是是用Texture Packer生成的JSON,loader會自動讀取數據,並對每個幀建立紋理。下面就是怎麼用loader來加載treasureHunter.json。當它成功加載,setup方法將會執行。
//路徑與實際項目有關
loader.add("images/treasureHunter.json").load(setup);
複製代碼
如今,紋理圖集上的每一個圖像都是Pixi緩存中的單個紋理。咱們可使用與Texture Packer中相同的名稱(「 blob.png」,「 dungeon.png」,「 explorer.png」等)訪問緩存中的每一個紋理。
Pixi提供了三種從紋理圖集建立精靈的方式:
1.使用TextureCache:
let texture = TextureCache["frameId.png"],
sprite = new Sprite(texture);
複製代碼
2.若是使用的是pixi的loader來加載紋理貼圖集,則使用loader的 resources屬性。
let sprite = new Sprite(resources["images/treasureHunter.json"].textures["frameId.png"]);
複製代碼
3.要建立一個精靈須要寫太多東西了!因此建議給紋理貼圖集的textures對象建立一個叫作id的別名,就像是這樣:
let id = PIXI.loader.resources["images/treasureHunter.json"].textures;
複製代碼
如今就能夠像這樣實例化一個精靈了:
let sprite = new Sprite(id["frameId.png"]);
複製代碼
這真的太棒了!
如下爲在setup函數中如何使用這三種不一樣的方法來建立和顯示dungeon,explorer,和treasure精靈。
//定義這三個變量,方便以後的使用
let textureId;
//地牢
let dungeon;
//探險者
let explorer;
//寶藏
let treasure;
//setup函數
function setup(){
//有3種不一樣的方式來建立和顯示精靈
//第一種,使用紋理別名,TextureCache爲PIXI.utils.TextureCache的別名
let dungeonTexture = TextureCache['dungeon.png'];
//Sprite爲PIXI.Sprite的別名
dungeon = new Sprite(dungeonTexture);
//調用addChild方法將精靈添加到舞臺中
app.stage.addChild(dungeon);
//第二種,使用resources來建立,也要注意參數根據實際狀況來寫
explorer = new Sprite(resources["images/treasureHunter.json"].textures['explorer.png']);
//將探險者的座標設置一下,也就是設置探險者的位置,探險者在舞臺中間,x方向距離隨便設置
explorer.x = 68;
explorer.y = app.stage.height / 2 - explorer.height / 2;
app.stage.addChild(explorer);
//爲全部的紋理圖集建立一個別名
textureId = PIXI.loader.resources['images/treasureHunter.json'].textures;
treasure = new Sprite(textureId["treasure.png"]);
//將寶藏的座標設置一下
treasure.x = app.stage.width - treasure.width - 48;
treasure.y = app.stage.height / 2 - treasure.height / 2;
//將寶藏精靈添加到舞臺中去
app.stage.addChild(treasure);
}
複製代碼
下圖爲以上代碼所展示的結果:
舞臺尺寸爲512 x 512像素,您能夠在上面的代碼中看到app.stage.height和app.stage.width屬性用於對齊精靈。 如下是瀏覽器的y位置垂直居中的方式:
explorer.y = app.stage.height / 2 - explorer.height / 2;
複製代碼
學習使用紋理圖集建立和顯示精靈是一個重要的基本操做。所以,在繼續以前,咱們再來編寫用於添加其他精靈的代碼:blobs和exit,這樣您即可以生成以下所示的場景:
如下是完成全部這些操做的所有代碼。還包括了HTML代碼,所以能夠在適當的上下文中查看全部內容。(能夠在此處下載代碼。)請注意,已建立了blobs精靈,並將其添加到循環中的舞臺上,並分配了隨機位置。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>從紋理圖中建立精靈</title>
</head>
<body>
<script src="https://www.eveningwater.com/static/data/web/PixiJS/source/dist/pixi.min.js"></script>
<script>
//別名
let Application = PIXI.Application,
Container = PIXI.Container,
loader = PIXI.loader,
resources = PIXI.loader.resources,
TextureCache = PIXI.utils.TextureCache,
Sprite = PIXI.Sprite,
Rectangle = PIXI.Rectangle;
//建立pixi應用對象
let app = new Application({
width: 512,
height: 512,
antialiasing: true,
transparent: false,
resolution: 1
});
//將應用對象添加到dom中
document.body.appendChild(app.view);
//加載json文件,並在加載完成以後執行setup函數,注意這裏的json文件路徑,後面的也是
loader.add("./texture.json").load(setup);
//定義一些須要用到的變量
let dungeon, explorer, treasure, door, textureId;
function setup() {
//如下分別使用三種不一樣的方式來建立精靈
//第一種
let dungeonTexture = TextureCache["dungeon.png"];
dungeon = new Sprite(dungeonTexture);
app.stage.addChild(dungeon);
//第二種
explorer = new Sprite(
resources["./texture.json"].textures["explorer.png"]
);
explorer.x = 68;
//設置探險者的位置
explorer.y = app.stage.height / 2 - explorer.height / 2;
app.stage.addChild(explorer);
//第三種
textureId = PIXI.loader.resources["./texture.json"].textures;
treasure = new Sprite(textureId["treasure.png"]);
//設置寶藏的位置
treasure.x = app.stage.width - treasure.width - 48;
treasure.y = app.stage.height / 2 - treasure.height / 2;
app.stage.addChild(treasure);
//建立出口的精靈
door = new Sprite(textureId["door.png"]);
door.position.set(32, 0);
app.stage.addChild(door);
//製做泡泡怪精靈
let numberOfBlobs = 6,//數量
spacing = 48,//位置
xOffset = 150;//偏移距離
//根據泡泡怪精靈的數量來製做精靈
for (let i = 0; i < numberOfBlobs; i++) {
let blob = new Sprite(textureId["blob.png"]);
let x = spacing * i + xOffset;
//隨機生成泡泡怪的位置
let y = randomInt(0, app.stage.height - blob.height);
// 設置泡泡怪的位置
blob.x = x;
blob.y = y;
//將泡泡怪添加到舞臺中
app.stage.addChild(blob);
}
}
//隨機生成的函數
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</script>
</body>
</html>
複製代碼
在線示例。
咱們能夠在上面的代碼中看到全部的blob都是使用for循環建立的。每一個blobs沿x軸均勻分佈,以下所示:
let x = spacing * i + xOffset;
blob.x = x;
複製代碼
spacing的值爲48,xOffset的值爲150。這意味着第一個Blob的x位置爲150。這會將其從舞臺的左側偏移150個像素。每一個後續的Blob的x值將比循環的上一次迭代中建立的Blob大48個像素。這樣沿着地牢地板從左到右建立了一條均勻分佈的怪物線。
每一個blob也被賦予一個隨機的y位置。如下爲執行此操做的代碼:
let y = randomInt(0, stage.height - blob.height);
blob.y = y;
複製代碼
能夠爲blob的y位置分配介於0到512之間的任何隨機數,512是stage.height的值。這在名爲randomInt的自定義函數的幫助下起做用。randomInt返回一個隨機數,該隨機數在您提供的任何兩個數字之間的範圍內。
//注意參數表明的意思
randomInt(lowestNumber, highestNumber);
複製代碼
這意味着,若是您想要一個介於1到10之間的隨機數,則能夠這樣得到:
let randomNumber = randomInt(1, 10);
複製代碼
如下是完成全部這些操做的randomInt函數定義:
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
複製代碼
randomInt是一個很好的用來作遊戲的工具函數,在寫遊戲的時候會常常用到它。
咱們如今知道了如何顯示精靈,可是如何使它們移動呢?這很容易:使用Pixi的代碼建立循環功能,這稱爲遊戲循環。放入遊戲循環中的任何代碼都會每秒更新60次。能夠編寫如下代碼來使cat精靈以每幀1個像素的速率向右移動。
function setup() {
//開始遊戲循環,建立一個這樣的函數
//Pixi的`ticker`提供了一個delta參數
app.ticker.add(delta => gameLoop(delta));
}
function gameLoop(delta){
//是cat精靈移動1px
cat.x += 1;
}
複製代碼
若是運行這段代碼,咱們將看到精靈逐漸移到舞臺的右側。
這是由於每次gameLoop運行時,它都會將cat的x位置加1。
cat.x += 1;
複製代碼
每個你放進Pixi的ticker的函數都會每秒被執行60次。你能夠看見函數裏面提供了一個delta的內容,他是什麼呢?delta的值表明幀的部分的延遲。能夠把它添加到cat的位置,讓cat的速度和幀率無關。下面是代碼:
cat.x += 1 + delta;
複製代碼
是否選擇添加此增量值在很大程度上是美學選擇。並且只有當您的動畫努力保持每秒60幀的一致顯示速率時,效果纔會真正顯着(例如,若是它在慢速設備上運行,則可能會發生這種狀況)。本文中的其他示例將不使用此增量值,但能夠根據須要在本身的工做中隨意使用它。 能夠沒必要使用Pixi的代碼來建立遊戲循環。若是願意的話,可使用requestAnimationFrame,以下所示:
function gameLoop() {
//每60秒執行一次遊戲循環函數
requestAnimationFrame(gameLoop);
//移動cat精靈
cat.x += 1;
}
//開始遊戲循環
gameLoop();
複製代碼
採用哪一種方式隨咱們本身的喜愛。這就是移動精靈全部的操做!只需在循環內以較小的增量更改任何sprite屬性,它們就會隨着時間推移進行動畫處理。若是要讓精靈沿相反的方向(向左)設置動畫,只需給它指定一個負值便可,例如-1。
如下是以上示例的完整代碼:
//別名
let Application = PIXI.Application,
Container = PIXI.Container,
loader = PIXI.loader,
resources = PIXI.loader.resources,
TextureCache = PIXI.utils.TextureCache,
Sprite = PIXI.Sprite,
Rectangle = PIXI.Rectangle;
//建立應用對象
let app = new Application({
width: 256,
height: 256,
antialias: true,
transparent: false,
resolution: 1
});
//將應用對象添加到dom中
document.body.appendChild(app.view);
// 加載圖像資源
loader.add("images/cat.png").load(setup);
//定義cat精靈
let cat;
function setup() {
//建立cat精靈
cat = new Sprite(resources["images/cat.png"].texture);
cat.y = 96;
app.stage.addChild(cat);
//開始遊戲循環
app.ticker.add(delta => gameLoop(delta));
}
function gameLoop(delta) {
// 移動1像素
cat.x += 1;
//也可使用增量
//cat.x += 1 + delta;
}
複製代碼
在線示例。
注意: (cat變量須要在setup和gameLoop函數以外定義,以即可以在兩個函數中進行訪問。)
咱們還能夠爲精靈的比例,旋轉或大小設置動畫-不管如何!咱們將看到更多有關如何爲精靈設置動畫的示例。
爲了方便本身,給遊戲增長點靈活性,最好使用兩個速度屬性(vx和vy)控制精靈的移動速度。vx用於設置精靈在x軸上(水平)的速度和方向。vy用於在y軸上(垂直)設置精靈的速度和方向。與其直接更改精靈的x和y值,不如先更新速度變量,而後將這些速度值分配給精靈。這是交互式遊戲動畫所需的額外的一個模塊。
第一步是在精靈上建立vx和vy屬性,併爲其賦予初始值。
cat.vx = 0;
cat.vy = 0;
複製代碼
設置vx和vy的值爲0意味着精靈並無被移動(即靜止)。接着,在遊戲循環中,更新咱們想要移動的vx和vy的速度值,而後把它們賦值給精靈的x和y屬性。如下是使用這種方式來使得精靈每幀往右下方移動1像素的示例代碼:
function setup() {
//建立cat精靈
cat = new Sprite(resources["images/cat.png"].texture);
cat.y = 96;
cat.vx = 0;
cat.vy = 0;
app.stage.addChild(cat);
//開始遊戲循環
app.ticker.add(delta => gameLoop(delta));
}
function gameLoop(delta){
//更新cat精靈的速度
cat.vx = 1;
cat.vy = 1;
//將速度屬性值賦值給cat精靈的位置,即x和y座標
cat.x += cat.vx;
cat.y += cat.vy;
}
複製代碼
當運行以上的代碼,cat精靈就會每幀往右下方移動1像素。以下圖所示:
想要讓cat精靈往不一樣方向移動嗎?讓cat精靈往左移動,那麼就將vx 的值設置爲負數,例如-1。想要讓它往上移動,那麼就將vy的值設置爲負數,例如-1。想要讓cat精靈移動的更快,只須要將vx和vy的值設置大一點,就像3,5,-2,-4。(負號表明方向)。
咱們會看到如何經過利用vx和vy的速度值來模塊化精靈的速度,它對遊戲的鍵盤和鼠標控制系統頗有幫助,並且更容易實現物理模擬。
在線示例。
考慮到樣式,也爲了幫助模塊化代碼,我的建議仍是像下面這樣構造遊戲循環:
//定義一個變量設置遊戲開始狀態
let state = play;
app.ticker.add((delta) => { gameLoop(delta)});
//開始遊戲循環
function gameLoop(delta){
//更改遊戲狀態
state(delta);
}
function play(delta){
cat.vx = 1;
cat.x += cat.vx;
}
複製代碼
咱們會看到gameLoop每秒60次調用了state函數。state函數是什麼呢?它被賦值爲play。意味着play函數會每秒運行60次。如下的示例展現瞭如何用一個新模式來重構上一個例子的代碼:
//爲了方便其它函數使用變量,將定義全局變量
let cat;
let state;
function setup() {
//建立cat精靈
cat = new Sprite(resources["images/cat.png"].texture);
cat.y = 96;
cat.vx = 0;
cat.vy = 0;
app.stage.addChild(cat);
//開始設置遊戲狀態
state = play;
//開始遊戲循環
app.ticker.add(delta => gameLoop(delta));
}
function gameLoop(delta){
//更新當前的遊戲狀態
state(delta);
}
function play(delta) {
//每幀讓cat移動1像素
cat.vx = 1;
cat.x += cat.vx;
}
複製代碼
是的,也許這有點讓人不快(head-swirler)!可是,不要讓它嚇到咱們,而是花一兩分鐘來思考這些功能是如何鏈接的。正如咱們將要看到的那樣,像這樣構造遊戲循環將使進行切換遊戲場景和關卡之類的事情變得很是容易。
在線示例。
只需多作一些工做,咱們就能夠構建一個簡單的系統來使用鍵盤控制精靈。爲了簡化咱們的代碼,建議使用稱爲keyboard的自定義函數來偵聽和捕獲鍵盤事件。以下所示:
function keyboard(value) {
let key = {};
key.value = value;
key.isDown = false;
key.isUp = true;
key.press = undefined;
key.release = undefined;
//鍵盤按下開始操做
key.downHandler = event => {
if (event.keyCode === key.value) {
if (key.isUp && key.press)key.press();
key.isDown = true;
key.isUp = false;
event.preventDefault();
}
};
//鍵盤按下結束
key.upHandler = event => {
if (event.keyCode === key.value) {
if (key.isDown && key.release)key.release();
key.isDown = false;
key.isUp = true;
event.preventDefault();
}
};
//綁定監聽的事件
const downListener = key.downHandler.bind(key);
const upListener = key.upHandler.bind(key);
window.addEventListener("keydown", downListener, false);
window.addEventListener("keyup", upListener, false);
//解綁事件的監聽
key.unsubscribe = () => {
window.removeEventListener("keydown", downListener);
window.removeEventListener("keyup", upListener);
};
return key;
}
複製代碼
keyboard函數用起來很容易,能夠像這樣建立一個新的鍵盤對象:
let keyObject = keyboard(keyValue);
複製代碼
它的一個參數是您要收聽的鍵值。能夠點擊此處查看鍵盤鍵值列表。而後將press和release方法分配給鍵盤對象,以下所示:
keyObject.press = () => {
//key object pressed
};
keyObject.release = () => {
//key object released
};
複製代碼
鍵盤對象也有isDown和isUp的布爾值屬性,用它們來檢查每一個按鍵的狀態。可是 不要忘記使用unsubscribe方法刪除事件偵聽器:
keyObject.unsubscribe();
複製代碼
在examples文件夾裏看一下keyboardMovement.html文件是怎麼用keyboard函數的,利用鍵盤的方向鍵去控制精靈圖。運行它,而後用上下左右按鍵去讓貓在舞臺上移動。
如下是全部的代碼:
let cat;
let state;
function setup(){
//建立cat精靈
cat = new Sprite(resource["./images/cat.png"].texture);
//設置cat精靈的座標
cat.y = 96;
cat.vx = 0;
cat.vy = 0;
//添加到舞臺中
app.stage.addChild(cat);
//鍵盤按鍵事件注意參數爲實際對應的鍵盤key,是整數數字
let left = keyboard("arrowLeft");
let right = keyboard("arrowRight");
let up = keyboard("arrowUp");
let down = keyboard("arrowDown");
//當按下左方向鍵往左改變速度,即改變vx爲負值,在這裏是5,vy不變
left.press = () => {
cat.vx = -5;
cat.vy = 0;
}
//當釋放左方向鍵時初始化速度
left.release = () => {
//若是右鍵沒有被按下,而且cat的vy速度爲0
if(!right.isDown && cat.vy === 0){
cat.vx = 0;
}
}
//當按下右方向鍵
right.press = () => {
cat.vx = 5;
cat.vy = 0;
}
//當釋放右方向鍵
right.release = () => {
if(!left.isDown && cat.vy === 0){
cat.vx = 0;
}
}
//當按下上方向鍵
up.press = () => {
cat.vy = -5;
cat.vx = 0;
}
//當釋放上方向鍵
up.release = () => {
if(!down.isDown && cat.vx === 0){
cat.vy = 0;
}
}
//當按下下方向鍵
down.press = () => {
cat.vy = 5;
cat.vx = 0;
}
//當釋放下方向鍵
down.release = () => {
if(!up.isDown && cat.vx === 0){
cat.vy = 0;
}
}
state = play;
//開始遊戲循環
app.ticker.add((delta) => {
gameLoop(delta);
})
}
function gameLoop(delta){
//更新遊戲狀態
state(delta);
}
function play(delta){
cat.x += cat.vx;
cat.y += cat.vy;
}
複製代碼
在線示例。
精靈分組使咱們能夠建立遊戲場景,並將類似的精靈做爲一個單元一塊兒管理。Pixi有一個名爲Container的對象,咱們可使用它來完成一些操做。讓咱們看看它是如何工做的。 假設您要顯示三種精靈:貓,刺蝟和老虎。建立它們並設置它們的位置-可是不要將它們添加到舞臺上。
//The cat
let cat = new Sprite(id["cat.png"]);
cat.position.set(16, 16);
//The hedgehog
let hedgehog = new Sprite(id["hedgehog.png"]);
hedgehog.position.set(32, 32);
//The tiger
let tiger = new Sprite(id["tiger.png"]);
tiger.position.set(64, 64);
複製代碼
接下來,建立一個animals容器以將它們所有分組,以下所示:
let animals = new Container();
複製代碼
而後使用addChild方法將這些精靈添加到分組容器中
animals.addChild(cat);
animals.addChild(hedgehog);
animals.addChild(tiger);
複製代碼
最後,把分組添加到舞臺中
app.stage.addChild(animals);
複製代碼
注: (stage對象也是一個Container。它是全部Pixi精靈的根容器。)
以上的代碼效果以下圖所示:
咱們是看不到這個包含精靈圖的animals分組的。它僅僅是個容器而已。
如今,咱們能夠將這個animals分組視爲一個單元。咱們也能夠將Container視爲一種沒有紋理的特殊精靈。若是須要獲取animals包含的全部子精靈的列表,可使用children數組來獲取。
console.log(animals.children)
//Displays: Array [Object, Object, Object]
複製代碼
它告訴咱們animals有三個子精靈。由於animals與任何其餘Sprite同樣,所以能夠更改其x和y值,alpha,scale和全部其餘sprite屬性。咱們在父容器上更改的任何屬性值都將以相對方式影響子精靈。所以,若是設置了animals的x和y位置,則全部子精靈將相對於animals的左上角從新定位。若是將animals的x和y位置設置爲64,會發生什麼?
animals.position.set(64, 64);
複製代碼
整個精靈組將向右下方移動64個像素。以下圖所示:
animals也有其本身的尺寸,該尺寸基於包含的精靈所佔據的面積。咱們能夠找到它的寬度和高度值,以下所示:
console.log(animals.width);
//Displays: 112
console.log(animals.height);
//Displays: 112
複製代碼
若是更改animals的寬度或高度會怎樣?
animals.width = 200;
animals.height = 200;
複製代碼
全部子精靈將縮放以匹配該更改。以下圖所示:
咱們能夠根據須要在其餘容器中嵌套儘量多的容器,以根據須要建立深層次結構。可是,DisplayObject(如Sprite或另外一個Container)一次只能屬於一個父級。若是使用addChild將精靈做爲另外一個對象的子級,則Pixi會自動將其從當前父級中刪除。這是咱們無需擔憂的有用的管理。
當把精靈添加到一個容器中時,它的x和y是相對於精靈分組的左上角來定位的,這就是精靈的局部位置。例如:你認爲cat精靈在如下圖中所處的位置是?
讓咱們來獲取它的值:
console.log(cat.x);
//Displays:16
複製代碼
16?是的!這是由於cat相對於分組的左上角只僅僅偏移了16像素而已。 16就是cat的局部位置。
精靈固然也有全局位置。全局位置就是舞臺左上角到精靈的錨點(一般值得是精靈的左上角的距離)的距離。咱們能夠經過toGlobal方法來獲取精靈的全局位置。以下:
//父精靈上的方法,傳入子精靈位置參數
parentSprite.toGlobal(childSprite.position)
複製代碼
以上代碼的意思就是若是咱們要在animals分組中找到cat精靈的全局位置,那麼就要像以下這樣寫代碼:
console.log(animals.toGlobal(cat.position));
//Displays: Object {x: 80, y: 80...};
複製代碼
而後它就會給咱們一個x和y的位置值,即80。更確切的說,cat的全局位置就是相對於舞臺左上角的位置。
若是不知道精靈的父容器是什麼?咱們如何找到精靈的全局位置呢?每一個精靈都會有一個parent屬性來告訴咱們它的父容器(或者說叫父精靈分組)是什麼。若是將一個精靈正確的添加到了舞臺中,那麼舞臺就是精靈的父容器。在以上的示例中,cat精靈的父容器就是 animals精靈分組。那也就意味着能夠經過編寫以下的代碼來獲取cat的全局位置。
cat.parent.toGlobal(cat.position);
複製代碼
即便咱們不知道cat精靈的父容器是誰,它同樣會執行。固然還有一種更好的方式來計算出精靈的全局位置,而且它一般也是一種最佳方式,因此聽好啦!若是咱們想要知道精靈到canvas左上角的距離,而且不知道或者不關心精靈的父容器是誰,可使用getGlobalPosition方法。如下展現瞭如何獲取tiger精靈的全局位置:
tiger.getGlobalPosition().x
tiger.getGlobalPosition().y
複製代碼
在咱們已經寫好的示例中,它會返回咱們x和y的值是128。更特別的是, getGlobalPosition返回的值很是精確:當精靈的局部位置改變的同時,也會返回給咱們準確的全局位置。
若是想要將全局位置轉爲局部位置應該怎麼辦?咱們可使用toLocal方法。它的工做方式很相似,一般是如下的格式:
sprite.toLocal(sprite.position, anyOtherSprite)
複製代碼
使用toLocal方法能夠找到一個精靈與另外一個任意精靈之間的距離。如下代碼展現瞭如何找到tiger相對於 hedgehog的位置。
tiger.toLocal(tiger.position, hedgehog).x
tiger.toLocal(tiger.position, hedgehog).y
複製代碼
上面的代碼會返回一個32的x值和一個32的y值。咱們能夠在示例圖中看到tiger的左上角和hedgehog的左上角距離32像素。
Pixi有一個額外的,高性能的方式去分組精靈的方法稱做:ParticleContainer(PIXI.particles.ParticleContainer)。任何在ParticleContainer裏的精靈都會比在一個普通的Container的渲染速度快2到5倍。這是用於提高遊戲性能的一個很棒的方法。 能夠像這樣建立ParticleContainer:
let superFastSprites = new PIXI.particles.ParticleContainer();
複製代碼
而後用addChild方法去往裏添加精靈,就像往普通的Container添加同樣。
若是使用ParticleContainer,咱們就不得不作出一些妥協。在一個ParticleContainer裏的精靈僅僅只有一些基本的屬性: x,y,width,height,scale,pivot,alpha, visible——就這麼多。並且,它包含的精靈不能擁有本身的嵌套子級ParticleContainer也沒法使用Pixi的高級視覺效果,例如濾鏡和混合模式。每一個ParticleContainer只能用一個紋理(所以,若是您想要具備不一樣外觀的精靈,則必須使用雪碧圖)。可是對於得到的巨大性能提高,這些妥協一般是值得的。並且,還能夠在同一項目中同時使用Containers和ParticleContainers,所以能夠微調優化。
爲何在Particle Container的精靈會如此快呢?由於精靈的位置是直接在GPU上計算的。Pixi開發團隊正在努力讓儘量多的雪碧圖在GPU上處理,因此頗有可能用的最新版的Pixi的 ParticleContainer的特性必定比如今在這兒描述的特性多得多。查看當前ParticleContainer文檔以獲取更多信息。
不管在哪裏建立一個ParticleContainer,都會有四個屬性參數須要提供: size,properties,batchSize,autoResize。
let superFastSprites = new ParticleContainer(maxSize, properties, batchSize, autoResize);
複製代碼
maxSize的默認值是1500。因此,若是須要包含更多的精靈,那就把這個值設爲更大的數字。 properties參數是一個對象,對象包含5個須要設置的布爾值:scale, position,rotation,uvs,alphaAndTint。position的默認值是true,其它4個參數的默認值是false。這意味着在ParticleContainer中,想要改變 scale,rotation,uvs,alphaAndTint,就不得不把這些屬性設置爲true,就像以下這樣:
let superFastSprites = new ParticleContainer(size,
{
rotation: true,
alphaAndtint: true,
scale: true,
uvs: true
}
);
複製代碼
可是,若是認爲不須要使用這些屬性,請將它們設置爲false能夠最大限度地發揮性能。什麼是uvs屬性?僅當具備在動畫時更改其紋理的粒子時,纔將其設置爲true。(全部精靈的紋理也都必須在同一雪碧圖上才能起做用。) (注意:UV映射是3D圖形顯示術語,指的是被映射到3D表面上的紋理(圖像)的x和y座標。U是x軸,V是y軸。WebGL已經使用x,y和z用於3D空間定位,所以選擇U和V表示2D圖像紋理的x和y。)
在線示例。
使用圖像紋理是製做精靈最有用的方法之一,可是也有其本身的低級繪製工具。可使用它們製做矩形,形狀,線,複雜的多邊形和文本。並且,幸運的是,它使用了與Canvas Drawing API幾乎相同的API,所以,若是已經熟悉canvas,就沒有什麼真正的新知識了。可是最大的好處是,與Canvas Drawing API不一樣,使用Pixi繪製的形狀由WebGL在GPU上渲染。Pixi能夠利用全部未開發的性能。讓咱們快速瀏覽一下如何製做一些基本形狀。下面是咱們將要使用的代碼來創造的圖形。
全部的形狀的初始化都是先創造一個Pixi的Graphics的類 (PIXI.Graphics)的實例。
let rectangle = new Graphics();
複製代碼
而後使用參數爲十六進制顏色代碼值的beginFill方法來設置矩形的填充顏色。如下是將其設置爲淺藍色的方法。
rectangle.beginFill(0x66CCFF);
複製代碼
若是想要給形狀設置一個輪廓,使用方法。如下爲給矩形設置一個4像素寬alpha值爲1的紅色輪廓的示例:
//第一個參數爲輪廓線寬度,第二個參數爲輪廓線顏色值,第三個參數爲alpha值
rectangle.lineStyle(4, 0xFF3300, 1);
複製代碼
使用drawRect方法來畫一個矩形,它的四個參數分別是x,y,width,height。
rectangle.drawRect(x, y, width, height);
複製代碼
使用endFill方法來結束繪製。就像Canvas Drawing API同樣!如下是繪製矩形,更改其位置並將其添加到舞臺所需的所有代碼。
let rectangle = new Graphics();
rectangle.lineStyle(4, 0xFF3300, 1);
rectangle.beginFill(0x66CCFF);
rectangle.drawRect(0, 0, 64, 64);
rectangle.endFill();
rectangle.x = 170;
rectangle.y = 170;
app.stage.addChild(rectangle);
複製代碼
以上代碼能夠在(170,170)這個位置創造一個寬高都爲64的藍色的紅框矩形。
使用drawCircle方法來創造一個圓。它的三個參數是x, y和radius。
drawCircle(x, y, radius)
複製代碼
與矩形和精靈不一樣,圓的x和y位置也是其中心點(圓點)。如下是製做半徑爲32像素的紫色圓圈的代碼。
let circle = new Graphics();
circle.beginFill(0x9966FF);
circle.drawCircle(0, 0, 32);
circle.endFill();
circle.x = 64;
circle.y = 130;
app.stage.addChild(circle);
複製代碼
做爲Canvas Drawing API的一個方面,Pixi容許您使用drawEllipse方法繪製橢圓。
drawEllipse(x, y, width, height);
複製代碼
x / y位置定義了橢圓的左上角(假設橢圓被一個不可見的矩形邊界框包圍-該框的左上角將表明橢圓的x / y錨點位置)。如下代碼繪製了一個黃色的橢圓,寬50像素,高20像素。
let ellipse = new Graphics();
ellipse.beginFill(0xFFFF00);
ellipse.drawEllipse(0, 0, 50, 20);
ellipse.endFill();
ellipse.x = 180;
ellipse.y = 130;
app.stage.addChild(ellipse);
複製代碼
Pixi還容許您使用drawRoundedRect方法制做圓角矩形。最後一個參數cornerRadius是一個數字(以像素爲單位),該數字肯定應將圓角設置爲多少。
drawRoundedRect(x, y, width, height, cornerRadius)
複製代碼
如下是繪製一個圓角爲10像素的矩形的代碼。
let roundBox = new Graphics();
roundBox.lineStyle(4, 0x99CCFF, 1);
roundBox.beginFill(0xFF9933);
roundBox.drawRoundedRect(0, 0, 84, 36, 10)
roundBox.endFill();
roundBox.x = 48;
roundBox.y = 190;
app.stage.addChild(roundBox);
複製代碼
從前面的例子咱們已經知道使用lineStyle方法來繪製一條線段了。與Canvas Drawing API同樣,咱們可使用moveTo和lineTo方法來畫線段的開始和結束點。如下代碼畫了一條4像素寬,白色的對角線。
let line = new Graphics();
line.lineStyle(4, 0xFFFFFF, 1);
line.moveTo(0, 0);
line.lineTo(80, 50);
line.x = 32;
line.y = 32;
app.stage.addChild(line);
複製代碼
PIXI.Graphics對象(如線條)具備x和y值,就像sprites同樣,所以繪製它們以後,能夠將它們放置在舞臺上的任何位置。
咱們還可使用drawPolygon方法將線鏈接在一塊兒並用顏色填充它們,以製做複雜的形狀。drawPolygon的參數是x / y點的路徑數組,這些點定義形狀上每一個點的位置。
let path = [
point1X, point1Y,
point2X, point2Y,
point3X, point3Y
];
graphicsObject.drawPolygon(path);
複製代碼
drawPolygon將這三個點鏈接在一塊兒以造成形狀。如下是使用drawPolygon將三條線鏈接在一塊兒以造成帶有藍色邊框的紅色三角形的方法。在位置(0,0)處繪製三角形,而後使用其x和y屬性將其移動到舞臺上的位置。
let triangle = new Graphics();
triangle.beginFill(0x66FF33);
triangle.drawPolygon([
-32, 64,
32, 64,
0, 0
]);
triangle.endFill();
triangle.x = 180;
triangle.y = 22;
app.stage.addChild(triangle);
複製代碼
在線示例。
使用Text對象(PIXI.Text)在舞臺上顯示文本。在最簡單的形式中,能夠這樣操做:
let message = new Text("Hello Pixi!");
app.stage.addChild(message);
複製代碼
這將在畫布上顯示單詞「Hello,Pixi」。Pixi的Text對象繼承自Sprite類,所以它們包含全部相同的屬性,例如x,y,width,height,alpha和rotation。 就像在其餘精靈上同樣,在舞臺上放置文本並調整其大小。例如,可使用position.set來設置消息的x和y位置,以下所示:
message.position.set(54, 96);
複製代碼
這將爲咱們提供基本的,無樣式的文本。可是,若是想變得更時髦,請使用Pixi的TextStyle(PIXI.TextStyle)函數來定義自定義文本樣式。如下爲示例代碼:
let style = new TextStyle({
fontFamily: "Arial",
fontSize: 36,
fill: "white",
stroke: '#ff3300',
strokeThickness: 4,
dropShadow: true,
dropShadowColor: "#000000",
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6,
});
複製代碼
這將建立一個新的樣式對象,其中包含要使用的全部文本樣式。有關可使用的全部樣式屬性的完整列表,請參見此處。 要將樣式應用於文本,請添加樣式對象做爲Text函數的第二個參數,以下所示:
let message = new Text("Hello Pixi!", style);
複製代碼
若是要在建立文本對象後更改其內容,可使用text屬性。
message.text = "Text changed!";
複製代碼
若是要從新定義樣式屬性,可使用style屬性。
message.style = {fill: "black", font: "16px PetMe64"};
複製代碼
Pixi經過使用Canvas Drawing API將文本呈現爲不可見的臨時畫布元素來製做文本對象。而後,它將畫布變成WebGL紋理,以即可以將其映射到精靈。這就是須要將文本的顏色包裹在字符串中的緣由:這是Canvas Drawing API的顏色值。與任何畫布顏色值同樣,可使用用於常見的顏色單詞,例如"red"或"green",也可使用rgba,hsla或hex顏色模式。Pixi還能夠包裝長行文本。將文本的wordWrap樣式屬性設置爲true,而後將wordWrapWidth設置爲文本行應達到的最大長度(以像素爲單位)。使用align屬性設置多行文本的對齊方式。以下例:
message.style = {wordWrap: true, wordWrapWidth: 100, align: center};
複製代碼
注: align不會影響單行文字。
若是要使用自定義字體文件,可使用CSS@font-face規則將字體文件連接到運行Pixi應用程序的HTML頁面。
@font-face {
font-family: "fontFamilyName";
src: url("fonts/fontFile.ttf");
}
複製代碼
將此@font-face規則添加到HTML頁面的CSS樣式表中。
Pixi還支持位圖字體。還可使用Pixi的loader來加載位圖字體XML文件,就像加載JSON或圖像文件同樣。
在線示例。
咱們如今知道了如何製做各類圖形對象,可是可使用它們作什麼呢?一個有趣的事情是構建一個簡單的碰撞檢測系統。可使用一個名爲hitTestRectangle的自定義函數,該函數檢查是否有兩個矩形Pixi精靈正在接觸。
hitTestRectangle(spriteOne, spriteTwo)
複製代碼
若是它們重疊(即碰撞),則hitTestRectangle將返回true。咱們能夠將hitTestRectangle與if語句一塊兒使用,以檢查兩個精靈之間的碰撞,以下所示:
if (hitTestRectangle(cat, box)) {
//There's a collision } else { //There's no collision
}
複製代碼
如咱們所見,hitTestRectangle是遊戲設計廣闊領域的門檻。 運行examples文件夾中的collisionDetection.html文件以獲取有關如何使用hitTestRectangle的工做示例。使用鍵盤上的方向鍵移動貓。若是貓碰到盒子,盒子會變成紅色,而後"hit!"將由文本對象顯示。
咱們已經看到了建立全部這些元素的全部代碼,以及使貓移動的鍵盤控制系統。惟一的新東西就是play函數內部使用hitTestRectangle來檢查碰撞的函數。
function play(delta) {
//使用cat精靈的速度屬性來移動
cat.x += cat.vx;
cat.y += cat.vy;
//檢查cat精靈與box精靈是否碰撞
if (hitTestRectangle(cat, box)) {
//若是碰撞則改變文本
//盒子顏色變成紅色
message.text = "hit!";
box.tint = 0xff3300;
} else {
//若是沒有碰撞重置文本與盒子顏色
message.text = "No collision...";
box.tint = 0xccff99;
}
}
複製代碼
因爲play函數每秒被遊戲循環調用60次,所以該if語句會不斷檢查貓和盒子之間的碰撞。 若是hitTestRectangle返回的是true,則文本消息對象使用文本顯示"hit!":
message.text = "Hit!";
複製代碼
而後,經過將盒子的tint屬性設置爲十六進制的紅色值,將盒子的顏色從綠色更改成紅色。
box.tint = 0xff3300;
複製代碼
若是沒有碰撞,則文本和盒子將保持其原始狀態:
message.text = "No collision...";
box.tint = 0xccff99;
複製代碼
這段代碼很是簡單,可是忽然之間建立了一個彷佛徹底活躍的交互式世界。幾乎就像魔術!並且,也許使人驚訝的是,咱們如今擁有開始使用Pixi製做遊戲所需的所有技能!
可是hitTestRectangle函數呢?它是作什麼的,它是如何工做的?這樣的碰撞檢測算法如何工做的細節不在本文的討論範圍以內。(若是真的想知道,能夠了解這本書的用法。)最重要的是,知道如何使用它。可是,僅供參考,以防萬一,也能夠參考完整的hitTestRectangle函數定義。咱們能從註釋中弄清楚它在作什麼?
function hitTestRectangle(r1, r2) {
//Define the variables we'll need to calculate let hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //hit will determine whether there's a collision
hit = false;
//Find the center points of each sprite
r1.centerX = r1.x + r1.width / 2;
r1.centerY = r1.y + r1.height / 2;
r2.centerX = r2.x + r2.width / 2;
r2.centerY = r2.y + r2.height / 2;
//Find the half-widths and half-heights of each sprite
r1.halfWidth = r1.width / 2;
r1.halfHeight = r1.height / 2;
r2.halfWidth = r2.width / 2;
r2.halfHeight = r2.height / 2;
//Calculate the distance vector between the sprites
vx = r1.centerX - r2.centerX;
vy = r1.centerY - r2.centerY;
//Figure out the combined half-widths and half-heights
combinedHalfWidths = r1.halfWidth + r2.halfWidth;
combinedHalfHeights = r1.halfHeight + r2.halfHeight;
//Check for a collision on the x axis
if (Math.abs(vx) < combinedHalfWidths) {
//A collision might be occurring. Check for a collision on the y axis
if (Math.abs(vy) < combinedHalfHeights) {
//There's definitely a collision happening hit = true; } else { //There's no collision on the y axis
hit = false;
}
} else {
//There's no collision on the x axis hit = false; } //`hit` will be either `true` or `false` return hit; }; 複製代碼
在線示例。
到此爲止,咱們如今已經擁有開始製做遊戲所需的全部技能。 什麼? 你不相信我嗎 讓我向你證實! 讓咱們來看看如何製做一個簡單的對象收集和避免敵人的遊戲,稱爲《尋寶獵人》。
《尋寶獵人》是可使用到目前爲止學到的工具製做的最簡單的完整遊戲之一的一個很好的例子。 使用鍵盤上的箭頭鍵可幫助探險家找到寶藏並將其帶到出口。六個Blob怪物在地牢壁之間上下移動,若是碰到了探索者,他將變成半透明, 而且右上角的血量進度條會縮小。若是全部血量都用光了,舞臺上會顯示「 You Lost!」; 若是探險家帶着寶藏到達出口,則顯示「 You Won!」。 儘管它是一個基本的原型,但《尋寶獵人》包含了您在大型遊戲中發現的大多數元素:紋理圖集圖形,交互性,碰撞以及多個遊戲場景。 讓咱們瀏覽一下游戲的組合方式,以即可以將其用做本身的一款遊戲的起點。
打開treasureHunter.html文件,你將會看到全部的代碼都在一個大的文件裏。 下面是一個關於如何組織全部代碼的概覽:
//建立pixi應用以及加載全部的紋理圖集的函數,就叫setup
function setup() {
//遊戲精靈的建立,開始遊戲狀態,開始遊戲循環
}
function gameLoop(delta) {
//運行遊戲循環
}
function play(delta) {
//全部的遊戲魔法都在這裏
}
function end() {
//遊戲最後所運行的代碼
}
//遊戲須要用到的工具函數:
//`keyboard`, `hitTestRectangle`, `contain`and `randomInt`
複製代碼
把這個看成你遊戲代碼的藍圖,讓咱們看看每一部分是如何工做的。
加載紋理圖集圖像後,setup函數即會運行。它僅運行一次,並容許您爲遊戲執行一次性設置任務。 在這裏建立和初始化對象,精靈,遊戲場景,填充數據數組或解析加載的JSON遊戲數據的好地方。 如下是Treasure Hunter中setup函數及其執行任務的簡要視圖。
function setup() {
//建立遊戲開始場景分組
//建立門精靈
//建立玩家也就是探險者精靈
//建立寶箱精靈
//創造敵人
//建立血量進度條
//添加一些遊戲所須要的文本顯示
//建立遊戲結束場景分組
//分配玩家的鍵盤控制器
//設置遊戲狀態
state = play;
//開始遊戲循環
app.ticker.add(delta => gameLoop(delta));
}
複製代碼
代碼的最後兩行,state = play;和gameLoop多是最重要的。 將gameLoop添加到Pixi的切換開關中能夠打開遊戲引擎, 並在連續循環中調用play函數。可是,在研究其工做原理以前,讓咱們先看看設置函數中的特定代碼是作什麼的。
setup函數將建立兩個容器組,分別稱爲gameScene和gameOverScene。這些都添加到舞臺中。
gameScene = new Container();
app.stage.addChild(gameScene);
gameOverScene = new Container();
app.stage.addChild(gameOverScene);
複製代碼
屬於主遊戲的全部精靈都添加到gameScene組中。遊戲結束時應顯示在遊戲上方的文本將添加到gameOverScene組。
儘管它是在setup函數中建立的,但當遊戲首次啓動時gameOverScene不該該可見,所以其visible屬性被初始化爲false。
gameOverScene.visible = false;
複製代碼
咱們將看到,當遊戲結束時,gameOverScene的visible屬性將設置爲true,以顯示出如今遊戲結束時的文本。
玩家,出口,寶箱以及地牢都是從紋理圖集中製做而來的精靈。十分重要的是,它們都被做爲gameScene的子精靈而添加。
//從紋理圖集中建立精靈
id = resources["images/treasureHunter.json"].textures;
//Dungeon
dungeon = new Sprite(id["dungeon.png"]);
gameScene.addChild(dungeon);
//Door
door = new Sprite(id["door.png"]);
door.position.set(32, 0);
gameScene.addChild(door);
//Explorer
explorer = new Sprite(id["explorer.png"]);
explorer.x = 68;
explorer.y = gameScene.height / 2 - explorer.height / 2;
explorer.vx = 0;
explorer.vy = 0;
gameScene.addChild(explorer);
//Treasure
treasure = new Sprite(id["treasure.png"]);
treasure.x = gameScene.width - treasure.width - 48;
treasure.y = gameScene.height / 2 - treasure.height / 2;
gameScene.addChild(treasure);
複製代碼
把它們都放在gameScene分組會使咱們在遊戲結束的時候去隱藏gameScene和顯示gameOverScene操做起來更簡單。
6個blob怪物是循環建立的。每一個blob都被賦予一個隨機的初始位置和速度。每一個blob的垂直速度交替乘以1或-1,這就是致使每一個blob沿與其相鄰方向相反的方向移動的緣由。每一個建立的blob怪物都會被推入稱爲blob的數組。
//泡泡怪數量
let numberOfBlobs = 6;
//泡泡怪水平位置值
let spacing = 48;
//泡泡怪偏移量
let xOffset = 150;
//泡泡怪速度
let speed = 2;
//泡泡怪移動方向
let direction = 1;
//一個數組存儲全部的泡泡怪
let blobs = [];
//開始建立泡泡怪
for (let i = 0; i < numberOfBlobs; i++) {
//建立一個泡泡怪
let blob = new Sprite(id["blob.png"]);
//根據`spacing`值將每一個Blob水平隔開
//xOffset肯定屏幕左側的點
//應在其中添加第一個Blob
let x = spacing * i + xOffset;
//給泡泡怪一個隨機的垂直方向上的位置
let y = randomInt(0, stage.height - blob.height);
//設置泡泡怪的位置
blob.x = x;
blob.y = y;
//設置泡泡怪的垂直速度。 方向將爲1或
//`-1。「 1」表示敵人將向下移動,「-1」表示泡泡怪將
//提高。將「方向」與「速度」相乘便可肯定泡泡怪
//垂直方向
blob.vy = speed * direction;
//下一個泡泡怪方向相反
direction *= -1;
//將泡泡怪添加到數組中
blobs.push(blob);
//將泡泡怪添加到gameScene分組中
gameScene.addChild(blob);
}
複製代碼
當咱們在玩尋寶獵人的時候,我想應該會發現,當咱們的探險者觸碰到任何一個敵人的時候,屏幕右上角的血量進度條的寬度都會減小。那麼這個血量進度條是如何製做的呢?它僅僅只是兩個相同位置重疊的矩形:一個黑色的矩形在後面,一個紅色的矩形在前面。它們都被分在healthBar分組中。healthBar被添加到gameScene分組中,而後在舞臺上被定位。
//建立血量進度條
healthBar = new PIXI.Container();
healthBar.position.set(stage.width - 170, 4)
gameScene.addChild(healthBar);
//建立黑色的矩形
let innerBar = new PIXI.Graphics();
innerBar.beginFill(0x000000);
innerBar.drawRect(0, 0, 128, 8);
innerBar.endFill();
healthBar.addChild(innerBar);
//建立紅色的矩形
let outerBar = new PIXI.Graphics();
outerBar.beginFill(0xFF3300);
outerBar.drawRect(0, 0, 128, 8);
outerBar.endFill();
healthBar.addChild(outerBar);
healthBar.outer = outerBar;
複製代碼
咱們已經看到一個被叫作outer的屬性被添加到healthBar中。它僅僅引用outerBar(紅色矩形),以便之後訪問時很方便。
healthBar.outer = outerBar;
複製代碼
咱們沒必要這樣作;可是,爲何不呢!這意味着,若是咱們想控制紅色outerBar的寬度,則能夠編寫一些以下所示的平滑代碼:
healthBar.outer.width = 30;
複製代碼
那很整潔並且可讀,因此咱們會保留它!
遊戲結束時,根據遊戲的結果,一些文本顯示「You won!」或「You lost!」。這是經過使用文本精靈並將其添加到gameOverScene來實現的。因爲遊戲開始時gameOverScene的visible屬性設置爲false,所以咱們看不到此文本。這是setup函數的代碼,該函數建立消息文本並將其添加到gameOverScene。
let style = new TextStyle({
//字體類型
fontFamily: "Futura",
//字體大小
fontSize: 64,
//字體顏色
fill: "white"
});
message = new Text("The End!", style);
message.x = 120;
message.y = app.stage.height / 2 - 32;
gameOverScene.addChild(message);
複製代碼
全部使精靈移動的遊戲邏輯和代碼都發生在play函數內部,該函數連續循環運行。這是play函數的概述:
function play(delta) {
//移動探險者並將其包含在地牢中
//移動泡泡怪
//檢測泡泡怪與探險者的碰撞
//檢測探險者與寶箱的碰撞
//檢測寶箱與出口的碰撞
//決定遊戲是贏仍是輸
//遊戲結束時,改變遊戲的狀態爲end
}
複製代碼
讓咱們找出全部這些功能的工做方式。
探險者是使用鍵盤控制的,執行該操做的代碼與先前學習的鍵盤控制代碼很是類似。鍵盤對象會修改探險者的速度,並將該速度添加到play函數中探險者的位置。
explorer.x += explorer.vx;
explorer.y += explorer.vy;
複製代碼
可是,新功能是探險者的動做被包含在地牢的牆壁內。綠色輪廓線顯示了探險者運動的極限。
這是在名爲contain的自定義函數的幫助下完成的。
contain(explorer, {x: 28, y: 10, width: 488, height: 480});
複製代碼
contain包含2個參數。第一個參數是你想要被包含的精靈,第二個參數則是任意的一個對象,包含x,y,width,height屬性,爲了定義一個矩形區域。在這個例子中,contain對象定義了一個僅比舞臺稍微偏移且小於舞臺的區域,它與地牢牆的尺寸所匹配。
如下是完成這些工做的contain函數。該函數檢查精靈是否已超出contain對象的邊界。 若是超出了,則代碼將精靈移回該邊界。contain函數還會返回碰撞變量,其值取決於"top","right","bottom","left",具體取決於擊中邊界的哪一側。(若是精靈沒有碰到任何邊界,則碰撞將是不肯定的。)
function contain(sprite, container) {
let collision = undefined;
//左
if (sprite.x < container.x) {
sprite.x = container.x;
collision = "left";
}
//上
if (sprite.y < container.y) {
sprite.y = container.y;
collision = "top";
}
//右
if (sprite.x + sprite.width > container.width) {
sprite.x = container.width - sprite.width;
collision = "right";
}
//下
if (sprite.y + sprite.height > container.height) {
sprite.y = container.height - sprite.height;
collision = "bottom";
}
//返回collision的值
return collision;
}
複製代碼
你將看到如何在前面的代碼中使用碰撞返回值,以使Blob怪物在上層和下層地下城牆之間來回反彈。
play函數還能夠移動Blob怪物,將它們保留在地牢壁中,並檢查每一個怪物是否與玩家發生碰撞。若是Blob撞到地牢的頂壁或底壁,則其方向會相反。全部這些都是在forEach循環的幫助下完成的,該循環遍歷每幀Blobs數組中的每一個Blob精靈。
blobs.forEach(function(blob) {
//移動泡泡怪
blob.y += blob.vy;
//檢查泡泡怪的屏幕邊界
let blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480});
//若是泡泡怪撞到舞臺的頂部或者底部,則方向反轉
//它的方向
if (blobHitsWall === "top" || blobHitsWall === "bottom") {
blob.vy *= -1;
}
//碰撞檢測若是任意敵人觸碰到探險者
//將探險者的explorerHit值設置爲true
if(hitTestRectangle(explorer, blob)) {
explorerHit = true;
}
});
複製代碼
咱們能夠在上面的代碼中看到contain函數的返回值如何用於使blob從牆反彈。名爲blobHitsWall的變量用於捕獲返回值:
let blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480});
複製代碼
blobHitsWall一般是undefined(未定義)的。可是,若是blob碰到了頂壁,則blobHitsWall的值將爲「top」。 若是blob碰到底壁,則blobHitsWall的值將爲「bottom」。若是以上兩種狀況均成立,則能夠經過反轉blob的速度來反轉blob的方向。如下是執行此操做的代碼:
if (blobHitsWall === "top" || blobHitsWall === "bottom") {
//經過改變速度爲負值來反轉方向
blob.vy *= -1;
}
複製代碼
將blob的vy(垂直速度)值乘以-1將翻轉其運動方向。
前面循環中的代碼使用hitTestRectangle函數來肯定是否有任何敵人觸摸了探險者。
if(hitTestRectangle(explorer, blob)) {
explorerHit = true;
}
複製代碼
若是hitTestRectangle返回的是true。也就意味着會發生一次碰撞而且explorerHit變量的值會是true。若是explorerHit的值是true,play函數將會使探險者變成半透明,而且血量進度條的寬度減小1像素。(具體減小多少依據每一個人本身定義。)。
if(explorerHit) {
//使探險者變成半透明
explorer.alpha = 0.5;
//減小血量進度條的寬度
healthBar.outer.width -= 1;
} else {
//使探險者徹底透明,若是不能再被撞擊
explorer.alpha = 1;
}
複製代碼
若是explorerHit爲false,則將explorer的alpha屬性保持爲1,這使其徹底不透明。play函數還檢查寶箱和探險者之間是否發生碰撞。若是有發生碰撞,寶藏將設置到探險者的位置,並稍有偏移。這使其看起來像探險家正在攜帶寶藏。
如下是完成這個工做的代碼:
if (hitTestRectangle(explorer, treasure)) {
//8的數字還能夠再大一點點
treasure.x = explorer.x + 8;
treasure.y = explorer.y + 8;
}
複製代碼
有兩種方式會讓遊戲結束:探險者攜帶寶箱併到達了出口就表示你贏了,或者就是你的血量進度條沒有了那就表示你失敗了。爲了贏得遊戲探險者僅僅只須要觸碰到出口,若是發生了這種狀況。那麼將遊戲的狀態state設置爲結束end,而後message也就是文本消息提示顯示"You won!"
if (hitTestRectangle(treasure, door)) {
state = end;
message.text = "You won!";
}
複製代碼
若是血量進度條沒有了,你也就遊戲失敗了。也將遊戲的狀態state設置爲結束end,而後message也就是文本消息提示顯示"You lost!"
if (healthBar.outer.width < 0) {
state = end;
message.text = "You lost!";
}
複製代碼
那麼如下代碼究竟是什麼意思呢?
state = end;
複製代碼
咱們從前面的示例中記住,gameLoop會以每秒60次的速度不斷更新稱爲狀態的函數。這是執行此操做的gameLoop:
function gameLoop(delta){
//更新當前的遊戲狀態
state(delta);
}
複製代碼
咱們還將記住,咱們最初將狀態值設置爲play,這就是爲何play函數循環運行的緣由。經過將狀態設置爲end,咱們告訴代碼咱們想要另外一個函數,稱爲end的循環運行。在更大的遊戲中,還能夠具備tileScene狀態,以及每一個遊戲級別的狀態,例如leveOne,levelTwo和levelThree。
end函數是什麼?如下即是:
function end() {
//遊戲場景不顯示,遊戲結束場景顯示
gameScene.visible = false;
gameOverScene.visible = true;
}
複製代碼
它只是翻轉游戲場景的可見性。這是隱藏gameScene並在遊戲結束時顯示gameOverScene的內容。 這是一個很是簡單的示例,說明了如何切換遊戲狀態,可是能夠在遊戲中擁有任意數量的遊戲狀態,並根據須要填充儘量多的代碼。只需將state的值更改成要在循環中運行的任何函數。 而這正是尋寶獵人的所有!只需多作一些工做,就能夠將這個簡單的原型變成完整的遊戲-試試吧!
在線示例。
到目前爲止,咱們已經學習瞭如何使用許多有用的精靈屬性,例如x,y,visible和rotation,這些屬性使咱們能夠大量控制精靈的位置和外觀。可是Pixi Sprites還具備許多更有趣的有用屬性。這是完整列表。 Pixi的類繼承系統如何運做?(什麼是類,什麼是繼承?單擊此連接以查找。)Pixi的sprites創建在遵循此鏈的繼承模型上:
DisplayObject > Container > Sprite
複製代碼
繼承只是意味着鏈中後面的類使用鏈中前面的類的屬性和方法。這意味着,即便Sprite是鏈中的最後一個類,它除了具備本身的獨特屬性外,還具備與DisplayObject和Container相同的全部屬性。 最基本的類是DisplayObject。任何DisplayObject均可以在舞臺上呈現。容器是繼承鏈中的下一個類。它容許DisplayObject充當其餘DisplayObject的容器。排名第三的是Sprite類。精靈既能夠顯示在舞臺上,也能夠做爲其餘精靈的容器。
(PS:本文基於官方教程而翻譯並加入了我的的理解以及示例,不喜勿噴。本文總結在本人的我的網站上)。