超快的HTML5 2D渲染引擎Pixi.js入門

PixiJS是一個2D渲染引擎,能自動偵測並使用WebGL或Canvas。

PixiJS使用JavaScript或HTML5基礎來顯示媒體,創建動畫或管理交互式圖像,從而製作遊戲或應用。

PixiJS適合製作遊戲但並非是遊戲引擎,其核心本質是儘可能快速有效地在屏幕上移動物體。

PixiJS作爲JavaScript的2D渲染器,目標是提供快速輕量且兼容所有設備的2D庫。提供無縫Canvas回退、支持主流瀏覽器,包括桌面和移動設備。

PIXI只做三件事

  • Loading and displaying of assets
  • interactivity
  • game loop

安裝測試

 

$ npm i pixi.js

引入

 

import * as PIXI from "pixi.js";
  • 開發工具 Vistual Code
  • Web服務器插件 Live Server
  • Chrome調試工具 PixiJS devtools

測試

判斷當前瀏覽器渲染引擎類型

 

$ vim index.html

 

<!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="https://cdn.bootcss.com/pixi.js/5.2.1/pixi.min.js"></script>
</head>
<body>
    <script>
        //判斷瀏覽器渲染引擎
        const type = PIXI.utils.isWebGLSupported() ? "WebGL" : "canvas";
        //console.log(type);//WebGL
        PIXI.utils.sayHello(type);//PixiJS 5.2.1 - WebGL - http://www.pixijs.com/
    </script>
</body>
</html>

判斷代碼

 

//判斷瀏覽器渲染引擎
const type = PIXI.utils.isWebGLSupported() ? "WebGL" : "canvas";
//console.log(type);//WebGL
PIXI.utils.sayHello(type);//PixiJS 5.2.1 - WebGL - http://www.pixijs.com/

概念

使用PIXI的基本流程通常分爲以下幾步

  1. 創建舞臺
  2. 創建畫布
  3. 將畫布添加到DOM
  4. 創建精靈
  5. 將精靈添加到畫布
  6. 將畫布添加舞臺
  7. 定時刷新舞臺

PIXI常用模塊包括

  • Application 應用
  • Container 容器
  • Stage 舞臺
  • Renderer 渲染器
  • Ticker 計時器
  • Loader 加載器

舞臺stage

  • 所有需要渲染的對象都必須添加到舞臺中才能被顯示出來
  • 舞臺處於整個樹形展示結構的對底層可理解爲背景

畫布renderer

畫布是指使用WebGL或Canvas繪圖引擎進行渲染的一塊區域

紋理texture

紋理可理解爲承載圖片的結構,紋理本身不能直接用於顯示,需要通過精靈才能顯示,類似DOM中的臨時碎片。

精靈sprite

精靈是可以直接用於舞臺顯示的對象,可理解爲DOM中的元素。精靈可直接食用圖片創建,也可以先創建紋理在使用紋理創建精靈。

事件event

事件用於交互

應用PIXI.Application

PIXI擁有一個Pixi應用對象PIXI.Application,可自動創建一個canvas標籤,用於計算圖片在其中的顯示。PIXI.Application會自動創建渲染器、計時器和根容器。

 

const app = new PIXI.Application(options);

PIXI.Application建立的同時會自動建立render、ticker、root container。

 

const app = new PIXI.Application({width:400, height:300, view:document.querySelector("canvas")});
  • app.renderer 渲染器,優先使用WebGL渲染若不支持則使用Canvas渲染。
  • app.ticker 渲染的更新頻率,計時器。
  • app.stage 根容器

例如:創建可顯示圖片的矩形顯示區域

 

//創建一個Pixi應用
const app = new PIXI.Application({width:400, height:300});
//pixi自動創建canvas標籤並添加到document文檔中
document.body.appendChild(app.view);

自動創建canvas標籤

 

<canvas width="400" height="300" style="touch-action: none; cursor: inherit;"></canvas>

使用PIXI.Application創建的pixi應用計算並選擇渲染引擎,這取決於瀏覽器自身所支持的渲染引擎的類型。創建pixi應用時需傳入options選項對象參數,options對象中可設置pixi應用的寬高、透明等屬性。若options對象中未設置view屬性,則會自動創建一個canvas元素,創建出來的canvas元素就在pixi應用的view屬性中。也可以使用view屬性手動指定。

 

const app = new PIXI.Application({width:400, height:300, view:document.querySelector("canvas")});
document.body.appendChild(app.view);
選項 類型 默認值 描述
width number 800 渲染器視寬度
height number 600 渲染視圖高度
view HTMLCanvasElement - 使用canvas作爲視圖
transparent boolean false 視圖是否透明
resolution number 1 分辨率,渲染器設備像素比,R屏默認爲2.
antialias boolean false 是否平滑抗鋸齒,取消字體平滑,抗混疊。
forceCanvas boolean false 是否強制使用canvas渲染模式,默認使用WebGL。
  • antialias可使字體的邊界和集合界面更爲圓滑,WebGL的anti-aliasing在所有平臺都不可用。
  • transparent會將整個canvas標籤的透明度進行設置
  • resolution會使pixi在不同分辨率和像素密度的設備上顯示正常
  • pixi的canvas畫布對象將會默認選擇WebGL引擎渲染模式,若需使用canvas引擎繪製可設置 forceCanvas。

適配分辨率

pixi會自動調整像素密度以匹配運行內容的設備的分辨率,開發人員所要做的是爲高分辨率和低分辨率提供不同的圖像。pixi將幫助你根據當前的設備像素比devicePixelRatio選擇對應的圖像。當創建高分辨率圖像時,可以將@2x添加到圖像文件名稱後,以說明圖像是支持高分辨率的屏幕,例如Retina屏幕。

獲取當前設備設備像素比,dpr是一個描述設備像素比的數字,由運行應用程序的設備自動提供,其中1表示爲標準分辨率,2表示高密度分辨率,3表示超高密度顯示器。

 

const dpr= window.devicePixelRatio;

 

const width = window.innerWidth;
const height = window.innerHeight;
const resolution = window.devicePixelRatio;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
document.body.appendChild(app.view);

pixi應用對象的成員

成員 類型 描述
app.loader PIXI.Loader 加載器實例用於資源加載,用於加載靜態資源。
app.renderer PIXI.Renderer|PIXI.CanvasRenderer WebGL渲染器,可使用CanvasRenderer。
app.resizeTo Window|HTMLElement 元素或窗口尺寸更改
app.screen PIXI.Rentangle 渲染器的屏幕矩形對象
app.stage PIXI.Container 根顯示容器,舞臺,pixi創建的根容器。遊戲中的精靈或容器都要添加到舞臺上才能顯示。
app.ticker PIXI.Ticker 計時器,可理解爲每次渲染一幀前執行。
app.view HTMLCanvasElement 渲染器的canvas元素,pixi創建的DOM元素即用於渲染遊戲的canvas。

修改canvas標籤背景色

 

app.renderer.backgroundColor = 0xff0000;

獲取畫布的寬度和高度

 

console.log(app.renderer.view.width);//400
console.log(app.renderer.view.height);//300

重新設置畫布的canvas寬高尺寸

 

app.renderer.autoResize = true;
app.renderer.resize(512, 512);
console.log(app.renderer.view.width);//512
console.log(app.renderer.view.height);//512

設置canvas佔滿整個窗口,需配置CSS重置padding和margin爲0。

 

*{padding:0;margin:0;}

 

app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);

渲染器app.renderer

獲取當前應用渲染器的類型

 

const rendererType = app.renderer.type;

 

const rendererType = PIXI.RENDERER_TYPE;
渲染模式 取值
UNKNOWN 0
WEBGL 1
CANVAS 2

渲染器選項參數

名稱 類型 默認值 描述
width number 800 屏幕寬度
height number 600 屏幕長度
view HTMLCanvasElement - canvas渲染模式需使用的視圖與選項
transparent boolean false 渲染器視圖是否透明

容器 PIXI.Container()

容器是用來轉載多個顯示對象的,創建的Pixi應用的stage屬性本質上就是一個容器對象,stage對象被當作根容器使用,它將包裹所有想用Pixi顯示的元素。

 

let container = new PIXI.Container();
container.addChild(sprite);

自定義的Container通常用來做分組使用,分組的好處在於修改Container的屬性,位於其中的子節點都會受到影響。

常見的作法是創建一個最頂層的rootContainer,之後所有的內容都添加到rootContainer中,rootContainer作爲頂級元素可進行縮放來適配不同的分辨率。

Container與Sprite有何異同

  • Container表示顯示對象的集合,是作爲其他對象的容器,是所有顯示對象的基類。
  • Container是用來轉載多個顯示對象,也包括自身Container。
  • Sprite表示基於紋理對象存在,是能將紋理顯示到舞臺的顯示對象。
  • Container和Sprite的繼承關係是PIXI.Sprite繼承自PIXI.Container。

Sprite是繼承自Container的,Container又是繼承自什麼對象呢?

Container繼承自DisplayObject,DisplayObject是最基本的顯示對象。

PIXI.Sprite -> PIXI.Container -> PIXI.DisplayObject

Sprite和Container共享DisplayObject的函數和方法

顯示對象 PIXI.DisplayObject

  • DisplayObject是所有渲染到屏幕上的對象的基類
  • DisplayObject是一個抽象類
成員屬性 類型 描述
x number X軸座標值
y number Y軸座標值
position PIXI.Point / PIXI.ObservablePoint 位移座標
alpha number 透明度
visible boolean 顯示隱藏
rotation number 旋轉角度
angle number 旋轉角度
scale PIXI.Point / PIXI.ObservablePoint 縮放值
skew PIXI.ObsevablePoint 斜切值
transform PIXI.TransformBase 矩陣對象
cacheAsBitmap boolean 是否將顯示對象轉換爲位圖
renderable boolean 是否能被渲染到舞臺

舞臺app.stage

創建pixi應用對象並添加到document中後就有了一個畫布,要在畫布上顯示東西就必須加入到一個稱爲舞臺的pixi對象pixi.stage

app.stage是一個Container的實例,作爲最底層的舞臺,所有需要渲染的圖形都應放到stage的內部。

pixi.stage舞臺是一個pixi容器對象,可理解爲將放入的東西分組並存儲的空箱子。stage舞臺對象是在場景總所有可見對象的根容器。所有放入到舞臺的東西都會被渲染到canvas中。

stage對象是Container容器,是所有Sprite精靈的根容器。

stage舞臺對象是pixi容器對象,所有的容器對象都具有相同的屬性和方法。儘管stage舞臺擁有width和height屬性,但不能查看畫圖窗口的大小。stage舞臺的width和height屬性僅僅告訴放入東西佔用的大小。

舞臺內科放入什麼東西呢?是稱作精靈的特殊圖像對象。精靈是能夠使用代碼控制的圖像,可以控制它的位置、大小等屬性來產生交互和動畫。

座標

  • 絕對座標global是以舞臺stage爲基準的座標系,精靈相對於舞臺的位置和方向。
  • 相對座標local是以舞臺stage中某個容器container爲基準的座標系,一般是精靈的父容器。

Pixi提供了兩個函數用於座標變換

  • toGlobal 相對座標轉換爲絕對座標
  • toLocal 絕對座標轉換爲相對座標

無論是容器還是精靈的座標以及方向都是相對於父容器的座標系的,座標原點默認是容器的左上角,可改變容器的座標原點anchor

加載 PIXI.Loader

將圖片加載到紋理緩存,因爲pixi使用WebGL和GPU渲染圖像,所以圖像需轉化成GPU可以處理的格式。可以被GPU處理的圖像稱爲紋理Texture。使用精靈顯示圖片之前,需將圖片轉化成WebGL紋理,pixi使用紋理緩存存儲和應用精靈所需的紋理。紋理的名字是圖像的地址。

從紋理緩存中獲取圖片

 

PIXI.utils.TextureCache["images/cat.png"];

紋理被以WebGL兼容的格式存儲起來,可使Pixi渲染更加高效。

例如:從紋理緩存中獲取圖片並轉化爲紋理

 

//將圖像加載到紋理緩存
let texture = PIXI.utils.TextureCache["images/avatar.jpg"];
//使用紋理創建精靈
let sprite = new PIXI.Sprite(texture);

如何加載圖像並將其轉化爲紋理呢?可使用pixi的Loader對象,pixi的Loader對象可加載任何種類的圖像資源,使用load方法加載圖像完成時可使用回調方法使用它。

 

const imgpath = "images/avatar.jpg";
PIXI.loader.add(imgpath).load(function(){
    let texture = PIXI.loader.resources[imgpath].texture;
    let sprite = new PIXI.Sprite(texture);
    app.stage.addChild(sprite);
});

紋理 PIXI.Texture

PixiJS使用WebGL或Canvas渲染圖像,所以圖像需要轉化成GPU能夠處理的格式,可以被GPU處理的圖像被稱爲紋理Texture。比如,當讓精靈顯示圖片之前,需要將普通的圖片轉化成WebGL紋理。爲了讓工作執行的快速有效率,PixiJS使用紋理緩存來存儲和應用精靈所需的紋理。紋理的名稱也就是圖像的地址。

紋理存儲着圖像或圖像的部分信息,圖像是不能直接添加到顯示列表中,必須將圖像轉化爲精靈的紋理。

從圖片獲取紋理

 

const texture = PIXI.Texture.from(imgPath);

遊戲是一種很耗資源的應用,特別是在移動設備中的遊戲。紋理圖集(Texture Atlas)又稱爲精靈表(Sprite Sheet)是將許多小的精靈圖片組合到一張大圖中。

精靈 PIXI.Sprite

PIXI的核心是Sprite,PIXI.Sprite繼承自PIXI.ContainerPIXI.Container繼承自PIXI.DisplayObject

Sprite對象是渲染到屏幕的所有紋理對象的基礎,它能輕鬆的改變紋理。

Sprite默認是不會響應點擊等事件的,必須需要Interactive。

pixi擁有一個sprite精靈類用來創建遊戲精靈,創建的方式有三種。

  • 使用單圖像文件創建
  • 使用雪碧圖來創建
  • 從紋理圖集中創建

創建Sprite的方式分爲

  • 常用方式

 

const sprite  = new PIXI.Sprite(texture?);
  • 快捷方式

 

const sprite = PIXI.Sprite.from(source);

參數source可以是URL地址、Canvas、紋理。

  • 快捷方式

 

const sprite = PIXI.Sprite.fromFrame(key);

參數key是JSON格式中的key或加載時定義的key。

  • 快捷方式

 

const sprite = PIXI.Sprite.fromImage(url, crossorigin?;

例如:

 

const imgpath = "images/avatar.jpg";
//將圖片添加到紋理緩存
PIXI.loader.add(imgpath).load(function(){
    //從紋理緩存中獲取紋理
    let texture = PIXI.loader.resources[imgpath].texture;
    //使用紋理創建精靈
    let sprite = new PIXI.Sprite(texture);
    //隱藏精靈
    sprite.visible = false;
    //將精靈添加舞臺
    app.stage.addChild(sprite);
    //將精靈從舞臺中刪除
    app.stage.removeChild(sprite);
});

使用別名

 

//設置別名
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, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//添加圖片
const img = "images/avatar.jpg";
loader.add(img).load(function(){
    let texture = resources[img].texture;
    let sprite = new Sprite(texture);
    app.stage.addChild(sprite);
});

監視加載進程

 

const name = "avatar";
const url = "images/avatar.jpg";
loader.add(name, url).load(function(){
    let texture = resources[name].texture;
    let sprite = new Sprite(texture);
    app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

add方法參數

 

add(name, url, optionObject, callbackFunction)
參數 類型 描述
name string 加載源文件別名
url string 源文件地址
options object literal 加載設置
options.crossOrigin boolean 源文件請求跨域
options.loadType string 原文加加載類型,默認爲Resource.LOAD_TYPE.XHR
options.xhrType string XHR處理方式,默認爲Resource.XHR_RESPONSE_TYPE.DEFAULT
callbackFunction function 加載完後執行的回調函數

add方法可傳入對象數組,也可使用鏈式加載。

 

const imgs = [
    {name:"avatar", url:"images/avatar.jpg"},  
    {name:"person", url:"images/person.png"},
    {name:"plane", url:"images/plane.bmp"},
];
loader.add(imgs).load(function(){
    let texture = resources["plane"].texture;
    let sprite = new Sprite(texture);
    app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

精靈位置

默認精靈sprite對象保存x和y屬性用來定位

 

let sprite = new Sprite(texture);
sprite.x = 100;
sprite.y = 100;

 

sprite.position.set(10, 10);

設置精靈的寬高大小

 

sprite.width = 150;
sprite.height = 120;

設置精靈的縮放比例

 

sprite.scale.x = 0.5;
sprite.scale.y = 0.5;

 

sprite.scale.set(0.5, 0.5);

scale的值從0到1表示對原精靈大小的百分比,1表示100%原來大小,0.5表示50%一般大小。

設置精靈的旋轉角度

可設置精靈的rotation來設定角度來旋轉,旋轉是針對錨點anchor發生的,默認在精靈的左上角,錨點是精靈旋轉的中心點。

 

sprite.rotation = 0.5;

設置紋理錨點anchor

 

sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;

錨點anchor取值範圍從0到1,是紋理長度或寬度的百分比。

設置精靈中心點pivot

精靈提供和anchor錨點類似的pivot屬性來設置精靈的原點,若改變pivot中心點的值後旋轉精靈,將會圍繞設置的原點來旋轉。

 

sprite.pivot.x = 100;
sprite.pivot.y = 100;
sprite.pivot.set(100, 100);

若精靈的尺寸爲200x200,設置pivot(100, 100)後,精靈將圍繞其中心點旋轉。

錨點pivot和中心點pivot的區別在於

  • anchor錨點改變了紋理的原點,使用0到1填充。
  • pivot中心點則精靈的原點,使用像素來填充。

雪碧圖SpriteSheet

雪碧圖是使用單個文件包含遊戲所需的所有圖片文件,pixi中使用雪碧圖中的小圖片的方式是使用一個矩形去截取紋理緩存中的對應位置來創建精靈。

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//創建應用
let app = new Application({width:256, height:256, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//添加圖片
const imgs = [
    {name:"altas", url:"images/altas.png"},
];
loader.add(imgs).load(function(){

    let texture = TextureCache["altas"];
    let rectangle = new Rectangle(0, 0, 64, 64);
    texture.frame = rectangle;

    let sprite = new Sprite(texture);
    sprite.position.set(150, 150);

    app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

Pixi內置通用Rectangle對象PIXI.Rectangle,用來定義矩形對象,矩形對象僅僅是一個數據對象。

 

let rectangle = new PIXI.Rectangle(x, y, width, height);

Pixi的紋理中存在frame屬性可被設置爲任何Rectangle矩形對象,frame將紋理映射到Rectangle的維度。

從雪碧圖創建精靈的紋理是一個使用頻繁的操作,Pixi提供更爲合適的方式來處理。

紋理貼圖

紋理貼圖集是一個JSON數據文件,包含了匹配的PNG雪碧圖的子圖像的大小和位置。使用紋理貼圖集時只需要知道對應名稱即可。修改或刪除圖片只需要修改JSON數據文件即可,遊戲內會自動給程序內所有數據應用更新紋理貼圖集。

Pixi可使用Texture Packer軟件輸出標準紋理貼圖集格式

 

const imgs = [
    {name:"man", url:"texture/man.json"},
];
loader.add(imgs).load(function(){

    //let texture = TextureCache["man1.jpg"];
    let texture = resources["man"].textures["man1.jpg"];
    let sprite = new Sprite(texture);
    sprite.position.set(150, 150);

    app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

例如:加載並創建紋理貼圖集並獲取其一居中顯示

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"man", url:"texture/man.json"},
];
//加載紋理貼圖集
loader.add(urls).load(function(){
    //獲取紋理創建精靈
    let texture = resources["man"].textures["man1.jpg"];
    let sprite = new Sprite(texture);
    //水平垂直居中定位
    const x = (width - sprite.width)/2;
    const y = (height - sprite.height)/2;
    sprite.position.set(x, y);
    //精靈添加到舞臺
    app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

精靈移動

使用pixi的ticker實現遊戲循環,任何在遊戲循環中的代碼都會1秒更新60次。

例如:讓hero精靈以每幀1像素的速率移動

 

//加載紋理貼圖集
loader.add(urls).load(function(){
    //獲取紋理創建精靈
    let texture = resources["man"].textures["hero1.png"];
    let sprite = new Sprite(texture);
    //水平垂直居中定位
    const x = (width - sprite.width)/2;
    const y = (height - sprite.height)/2;
    sprite.position.set(x, y);
    sprite.scale.set(0.5, 0.5);
    sprite.vx = 1;
    sprite.vy = 1;
    //精靈添加到舞臺
    app.stage.addChild(sprite);
    //精靈移動
    app.ticker.add(delay=>{
        console.log(sprite.y, sprite.vy);
        if(sprite.x < height/2){
            sprite.x += sprite.vx + delay;
            sprite.y += sprite.vy + delay;
        }
        
    });
}).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});

動畫精靈 PIXI.AnimatedSprite

  • 動畫精靈就是逐幀動畫,是通過 一幀一幀的播放圖像來產生運動的錯覺。
  • 動畫精靈使按一系列略不相同的圖像創建精靈,然後一幀一幀播放這些圖像產生運動的幻覺。
  • 製作動畫精靈需使用PIXI的AnimatedSprite方法

例如:將序列幀圖片使用Texture Packer製作成紋理貼圖集並導出JSON文件後製作精靈動畫

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.AnimatedSprite;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"effect", url:"texture/effect.json"},
];
//加載紋理貼圖集
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//啓動代碼
function setup(){
    //獲取紋理創建精靈
    let textures = resources["effect"].textures;
    //對象轉數組
    const arrTextures = Object.values(textures);
    //創建精靈動畫
    sprite = new AnimatedSprite(arrTextures);
    sprite.animationSpeed = 1;
    //水平垂直居中定位
    const x = (width - sprite.width)/2;
    const y = (height - sprite.height)/2;
    sprite.position.set(x, y);
    //精靈添加到舞臺
    app.stage.addChild(sprite);
    //播放精靈動畫
    sprite.play();
};

PIXI.AnimatedSpirte

使用紋理數組創建動畫精靈

 

const sprite = new PIXI.AnimatedSprite(textures, autoUpdated);
參數 類型 默認值 描述
textures array - 紋理貼圖數組
autoUpdated boolean true 是否使用PIXI.ticker.shared自動更新動畫時間

返回值:返回用於控制動畫精靈的對象

返回值屬性 類型 描述
animationSpeed number 播放速度默認爲1,值越大速度越快。
currentFrame number 當前幀編號
totalFrames number 幀總數
playing boolean 當前動畫是否在播放
loop boolean 是否循環播放
onLoop function loop爲true時調用
onComplete function 當loop爲false時動畫完成後調用
onFrameChange function 但動畫更改要呈現的紋理時調用
textures array 紋理數組
返回值方法 參數 描述
play - 播放的動畫精靈
stop - 停止播放動畫精靈
gotoAndPlay frameNumber, numberType, frameIndex 轉到特定幀並開始播放動畫
gotoAndStop frameNumber, numberType, frameIndex 轉到特定幀並停止播放動畫

平鋪精靈PIXI.TilingSprite

平鋪精靈是一種特殊的精靈,可在一定的範圍內重複一個紋理。可使用平鋪精靈創建無線滾動的背景效果。

創建平鋪精靈

 

const TilingSprite = PIXI.TilingSprite;
const tiling = new TilingSprite(texture, width, height);
參數 類型 默認值 描述
texture PIXI.Texture - 平鋪精靈紋理
width number 100 平鋪精靈的寬度
height number 100 平鋪精靈的高度

平鋪精靈具有和普通精靈相同的屬性,與普通精靈工作方式相同。

例如:使用小圖創建平鋪精靈並移動

brick.png

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let Container = PIXI.Container;
let Sprite = PIXI.Sprite;
let AnimatedSprite = PIXI.AnimatedSprite;
let TilingSprite = PIXI.TilingSprite;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"brick", url:"images/brick.png"},
];
//加載紋理
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
//啓動代碼
let tiling;
function setup(){
    //創建平鋪精靈
    const texture = resources["brick"].texture;
    tiling = new TilingSprite(texture, width, height);
    tiling.tileScale.set(0.5, 0.5);
    app.stage.addChild(tiling);

    app.ticker.add(delay=>loop(delay));
};

let state = play;
function loop(delay){
    state(delay);
}

function play(delay){
    tiling.tilePosition.x -= 1;
}

例如:視差滾動的僞3D效果

wood.jpg

ground.png

在同一位置層疊多個平鋪精靈,遠景圖片移動要慢於近景圖像。

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let Container = PIXI.Container;
let Sprite = PIXI.Sprite;
let AnimatedSprite = PIXI.AnimatedSprite;
let TilingSprite = PIXI.TilingSprite;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"wood", url:"images/wood.jpg"},
    {name:"ground", url:"images/ground.png"},
];
//加載紋理
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
//啓動代碼
let wood, ground;
function setup(){
    const woodTexture = resources["wood"].texture;
    wood = new TilingSprite(woodTexture, width, height);

    const groundTexture = resources["ground"].texture;
    ground = new TilingSprite(groundTexture, width, groundTexture.height);
    ground.y = height - groundTexture.height;

    app.stage.addChild(wood);
    app.stage.addChild(ground);

    app.ticker.add(delay=>loop(delay));
};

let state = play;
function loop(delay){
    state(delay);
}

function play(delay){
    wood.tilePosition.x -= 1;
    ground.tilePosition.x += 2;
}

精靈狀態SpriteUtilities

當具有複雜的遊戲角色或交互式對象時,可能希望角色根據遊戲環境中發生的動作,以不同的方式運行。每個單獨的行爲稱爲狀態,如果在精靈上定義狀態,只要遊戲中出現與該狀態相應的事件時,就可以觸發這些狀態。比如,通過鍵盤控制角色移動。

使用精靈狀態,首先需要狀態播放器,狀態播放器用於控制精靈的狀態。pixi精靈沒有自己的狀態播放器,可使用SpriteUtilities庫中的sprite方法,sprite方法將創建一個內置狀態播放器的精靈。

遊戲狀態

遊戲狀態作爲一種代碼風格,幫助模塊組織代碼,結構化遊戲循環代碼,使場景切換和關卡類操作變得更加簡單。

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"man", url:"texture/man.json"},
];

let state;//遊戲狀態

//加載紋理貼圖集
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//啓動代碼
function setup(){
    //獲取紋理創建精靈
    let texture = resources["man"].textures["hero1.png"];
    sprite = new Sprite(texture);
    //水平垂直居中定位
    const x = (width - sprite.width)/2;
    const y = (height - sprite.height)/2;
    sprite.position.set(x, y);
    sprite.scale.set(0.5, 0.5);
    sprite.vx = 1;
    sprite.vy = 1;
    //精靈添加到舞臺
    app.stage.addChild(sprite);

    //精靈移動
    app.ticker.add(delay=>loop(delay));
};

//設置遊戲狀態
state = play;

//遊戲環迅
function loop(delay){
    state(delay);
};

//運行狀態
function play(delay){
    console.log(sprite.y, sprite.vy);
    if(sprite.x < height/2){
        sprite.x += sprite.vx + delay;
        sprite.y += sprite.vy + delay;
    }
};

遊戲狀態機

遊戲狀態機是用於設計遊戲的基本狀態邏輯

例如

 

const GAME_STATUS = {UNINIT:0, INIT:1, LOADED:2, PLAYING:3, GAMEOVER:4};

消息總線

監聽器模式是一個減少組件之間耦合的辦法,不同組件之間通過消息總線收發消息,可以有效地避免組件之間的複雜調用。遊戲是遊戲互動多狀態多的情況。比如遊戲裝狀態機從LOADED轉變爲PLAYING時廣播這個消息給相關組件遊戲開始了。

鍵盤控制

自定義keyboard鍵盤函數用於監聽和捕獲鍵盤事件

編寫鍵盤方法

 

const keycode = {
    left:37,
    up:38,
    right:39,
    down:40    
};

function keyboard(code){
    let key = {};
    key.code = code;
    //key status
    key.isDown = false;
    key.isUp = true;
    //key action
    key.press = undefined;
    key.release = undefined;
    //key down handler
    key.down = evt=>{
        if(evt.keyCode === key.code){
            if(key.isUp && key.press){
                key.press();
            }
            key.isDown = true;
            key.isUp = false;
        }
        evt.preventDefault();
    };
    // key up handler
    key.up = evt=>{
        if(evt.keyCode===key.code){
            if(key.isDown && key.release){
                key.release();
            }
            key.isUp = true;
            key.isDown = false;
        }
        evt.preventDefault();
    };

    //attach event listeners
    window.addEventListener("keydown", key.down.bind(key), false);
    window.addEventListener("keyup", key.up.bind(key), false);

    return key;
}

使用鍵盤控制角色的移動

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.extras.AnimatedSprite;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"effect", url:"texture/effect.json"},
];
//加載紋理貼圖集
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//啓動代碼
function setup(){
    //獲取紋理創建精靈
    let textures = resources["effect"].textures;
    //對象轉數組
    const arrTextures = Object.values(textures);
    //創建精靈動畫
    sprite = new AnimatedSprite(arrTextures);
    sprite.animationSpeed = 1;
    //水平垂直居中定位
    const x = (width - sprite.width)/2;
    const y = (height - sprite.height)/2;
    sprite.position.set(x, y);
    //移動速度
    sprite.vx = 0;
    sprite.vy = 0;

    //精靈添加到舞臺
    app.stage.addChild(sprite);
    //播放精靈動畫
    sprite.play();
    //設置遊戲狀態
    state = play;
    //循環播放
    app.ticker.add(delta=>loop(delta));
};

function loop(delta){
    state(delta);
}
function play(delta){
    sprite.x += sprite.vx;
    sprite.y += sprite.vy;
}
//鍵盤方向鍵
const keyUp = keyboard(keycode.up); 
const keyDown = keyboard(keycode.down); 
const keyLeft = keyboard(keycode.left); 
const keyRight = keyboard(keycode.right); 
//鍵盤控制
keyUp.press = function(){
    sprite.vx = 0;
    sprite.vy = -5;
};
keyUp.release = function(){
    if(sprite.vx===0 && !keyDown.isDown){
        sprite.vy = 0;
    }
};
keyDown.press = function(){
    sprite.vx = 0;
    sprite.vy = 5;
};
keyDown.release = function(){
    if(sprite.vx===0 && !keyUp.isDown){
        sprite.vy = 0;
    }
};
keyLeft.press = function(){
    sprite.vx = -5;
    sprite.vy = 0;
};
keyLeft.release = function(){
    if(sprite.vy===0 && !keyRight.isDown){
        sprite.vx = 0;
    }
};
keyRight.press = function(){
    sprite.vx = 5;
    sprite.vy = 0;
};
keyRight.release = function(){
    if(sprite.vy === 0 && !keyLeft.isDown){
        sprite.vx = 0;
    }
};

容器PIXI.Container

容器可以用來管理相似的多個精靈

例如:將不同精靈放入容器後添加到舞臺

 

//設置別名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.AnimatedSprite;
let Container = PIXI.Container;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//資源位置
const urls = [
    {name:"hero1", url:"images/hero1.png"},
    {name:"hero2", url:"images/hero2.png"},
];
//加載紋理
loader.add(urls).load(setup).on("progress", function(loader, resource){
    console.log("loading", loader.progress, resource.name, resource.url);
});
//啓動代碼
function setup(){
    //從紋理緩存中創建精靈
    let hero1 = new Sprite(resources["hero1"].texture);
    let hero2 = new Sprite(resources["hero2"].texture);
    //縮放精靈
    hero1.scale.set(0.5, 0.5);
    hero2.scale.set(0.5, 0.5);
    //創建容器並添加元素
    let container = new Container();
    container.addChild(hero1);
    container.addChild(hero2);
    //設置容器在場景中的位置
    const x = (width - container.width)/2;
    const y = (height - container.height)/2;
    container.position.set(x, y);
    //將容器添加到舞臺
    app.stage.addChild(container);
};

當往一個Container容器中添加精靈時,其x和y的位置是相對於分組左上角,這是精靈的局部位置。精靈還具有一個全局位置,全局位置是舞臺左上角到精靈錨點(通常是精靈左上角)的距離,可通過toGlobal方法獲取精靈圖的全局位置。

粒子容器PIXI.ParticleContainer

Pixi有一個額外的、高性能的容器稱爲ParticleContainer,任何在ParticleContainer中的精靈都會比在普通Container的渲染速度快2到5倍,這是用於提升遊戲性能的一個很不錯的方式。

爲什麼ParticleContainer中的精靈圖能處理的這麼快呢?因爲精靈的位置是直接在GPU上計算的。Pixi開發團隊正努力讓更多的雪碧圖在GPU上處理。

創建粒子容器

 

const ParticleContainer = PIXI.ParticleContainer;
const particleContainer = new ParticleContainer(options);

創建ParticleContainer時可以傳遞四個參數size、properties、batchSize、autoResize。

  • ParticleContainer中的精靈只有Container中少部分屬性x、y、width、height、scale、pivot、alpha、visible。
  • ParticleContainer包含的精靈不能再繼續嵌套。
  • ParticleContainer不能使用Pixi中的視覺效果,比如過濾器和混合模式。
  • 每個ParticleContainer只能使用一個紋理,如果想讓精靈有不同的表現形式,則必須更換雪碧圖。

幾何圖形PIXI.Graphics

使用圖片紋理製作精靈使最有效的方式之一,Pixi提供了自己的繪圖工具用來創造矩形、線段、多邊形以及文本。

Pixi的繪圖工具和Canvas Drawing API幾乎一致,不同之處在於Pixi的繪圖API是通過WebGL在GPU上渲染的。

所有圖形的初始化都需要先創建一個Pixi的Graphics類的實例

 

const Graphics = PIXI.Graphics;
const graphics = new Graphics();

矩形

繪製矩形

 

graphics.drawRect(x, y, width, height);

例如:繪製100x100,背景黑色,3像素紅色邊框的矩形。

 

//設置別名
let Application = PIXI.Application;
let Graphics = PIXI.Graphics;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = window.devicePixelRatio;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//創建矩形
let graphics = new Graphics();
graphics.beginFill(0x000000);
graphics.lineStyle(3, 0xff0000, 1);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
graphics.x = (width - graphics.width)/2;
graphics.y = (height - graphics.height)/2;
app.stage.addChild(graphics);

圓角矩形

 

graphics.drawRoundedRect(x, y, width, height, cornerRadius);

圓形

 

graphics.drawCircle(x, y, radius);

橢圓

 

graphics.drawEllipse(x, y, width, height);

顯示文本PIXI.Text

Pixi使用Text對象在舞臺上顯示文本,Text文本對象繼承自Sprite類,可以像處理精靈一樣在舞臺上定位和調整文本。

 

//設置別名
let Application = PIXI.Application;
let Text = PIXI.Text;
let TextStyle = PIXI.TextStyle;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = 1;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置滿屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//創建文本並設置樣式
const 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
});
let str = "Hello World";
const text = new Text(str, style);
text.text = "HELLO WORLD";

let x = (width - text.width)/2;
let y = (height - text.height)/2;
text.position.set(x, y);
app.stage.addChild(text);

碰撞檢測hitTestRectangle

自定義hitTestRectangle函數用於檢測兩個矩形是否接觸

 

const boolean = hitTestRectangle(sprite1, sprite2);

若精靈1和精靈2發生重疊則返回true,反之爲false。

 

//矩形碰撞檢查
function hitTestRectangle(r1, r2){
    let hit = false;//是否發生碰撞

    //獲取矩形中心點
    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;
    //計算矩形中心點距離
    const vx = Math.abs(r1.centerX - r2.centerX);
    const vy = Math.abs(r1.centerY - r2.centerY);
    //計算矩形合併時尺寸
    const combinedHalfWidth = (r1.width + r2.width)/2;
    const combinedHalfHeight = (r1.height + r2.height)/2;
    //檢測矩形是否發生碰撞
    if(combinedHalfWidth>vx && combinedHalfHeight>vy){
        hit = true;
    }
    
    return hit;
}

例如:繪製兩個矩形,測試是否發生碰撞。

//設置別名
let Application = PIXI.Application;
let Rectangle = PIXI.Rectangle;
let Graphics = PIXI.Graphics;
//創建應用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = 1;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//設置別名
const renderer = app.renderer;
const stage = app.stage;
//設置滿屏
renderer.view.style.position = "absolute";
renderer.view.style.display = "block";
renderer.autoResize = true;
renderer.resize(window.innerWidth, window.innerHeight);
//創建矩形
const r1 = new Graphics();
r1.beginFill(0x00ffff);
r1.drawRect(0, 0, 100, 100);
r1.endFill();
r1.position.set((width-r1.width)/2, (height - r1.height)/2);
stage.addChild(r1);

const r2 = new Graphics();
r2.beginFill(0x0000ff);
r2.drawRect(0, 0, 100, 100);
r2.endFill();
r2.position.set((width-r2.width)/2, (height - r2.height)/2+10);
stage.addChild(r2);

//判斷舉行是否發生碰撞
const hit = hitTestRectangle(r1, r2);
console.log(hit);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章