Starling 2D框架简介
本系列是對(duì)Introducing Starling pdf的翻譯,下文是對(duì)adobe開(kāi)發(fā)人員中心的一片日志的轉(zhuǎn)載,地址為http://www.adobe.com/cn/devnet/flashplayer/articles/introducing_Starling.html
Starling 是在 Stage3D APIs 基礎(chǔ)上開(kāi)發(fā)的一種 ActionScript 3 2D 框架(可用于 Flash Player 11 和 Adobe AIR 3的桌面)。 Starling 是為游戲開(kāi)發(fā)設(shè)計(jì)的,但是你可以將它應(yīng)用于很多其它的應(yīng)用程序。 在不必涉及低級(jí) Stage3D APIs 情況下,Starling 使得編寫(xiě)具有快速 GPU 加速功能的應(yīng)用程序成為可能。
大多數(shù) Flash 開(kāi)發(fā)人員希望利用這種能力提高 GPU 的加速功能(通過(guò)使用 Stage3D 技術(shù)),而不必編寫(xiě)如此高級(jí)的框架和深入研究低級(jí)的 Stage3D APIs。 Starling 是完全基于 Flash Player APIs 而設(shè)計(jì),并將 Stage3D(Molehill)復(fù)雜性抽象化。 因此每個(gè)人都能看到直觀的程序。
Starling 是為 ActionScript 3開(kāi)發(fā)人員而設(shè)計(jì),尤其是這些涉及2D游戲開(kāi)發(fā)的人員。 在使用 ActionScript 3 之前,你必須基本了解它。由于 Starling 輕便、靈活并易于使用,你也可以將它應(yīng)用于其它項(xiàng)目需求,例如 UI 編程。 這種框架要求設(shè)計(jì)得越直觀越好,因此任何 Java? 或者.Net? 開(kāi)發(fā)人員都可以馬上開(kāi)始使用它。
Starling使用概述
Starling 直觀并易于使用。Flash 和 Flex 開(kāi)發(fā)人員能夠快速地了解它,因?yàn)樗裱蠖鄶?shù) ActionScript 規(guī)則并將低級(jí) Stage3D APIs 的復(fù)雜性抽象化。Starling 使用熟知的概念,例如DOM顯示列表、事件模型以及熟知的如 MovieClip、Sprite、TextField 等APIs,而不是依靠諸如頂點(diǎn)緩沖(vertices buffer)、透視矩陣(perspective matrices)、著色程序(shader programs)和組合字節(jié)碼(assembly bytecode)進(jìn)行編碼。
Starling在很多領(lǐng)域都很輕便。 類的數(shù)量是有限的(大概有80k的代碼)。 除了Flash Player 11 或者 AIR 3(以及在未來(lái)的版本中使用的移動(dòng)支持)之外,它沒(méi)有外部依賴。 這些因素使得你的應(yīng)用程序很小并使你的工作流程簡(jiǎn)單。
Starling能夠免費(fèi)使用并富有朝氣。 它根據(jù) Simplified BSD 許可獲得授權(quán),因此你可以免費(fèi)地使用它,即便是在商業(yè)應(yīng)用程序中也是如此。 我們每天都在使用它并且我們依靠一個(gè)活躍的團(tuán)隊(duì)不斷地完善它。
在后臺(tái)操作中,Starling 使用 Stage3D APIs —它們是在桌面上基于 OpenGL 和 DirectX ,在移動(dòng)設(shè)備上基于 OpenGL ES2而運(yùn)行的低級(jí)的 GPU APIs。 需要重點(diǎn)注意的是,Starling 是?Sparrow?的 ActionScript 3 端口,它等同于基于 OpenGL ES2 APIs 的ISO庫(kù)(參見(jiàn)圖1):
圖1. Stage3D (Molehill) 分層位于 Starling 之上Starling 重新創(chuàng)建了很多 Flash 開(kāi)發(fā)人員熟知的 APIs 。 下圖列舉了通過(guò) Starling 暴露的圖形元素 APIs(參見(jiàn)圖2)。
圖2. Starling支持DisplayObject繼承在 3D GPU APIs 基礎(chǔ)上可以創(chuàng)建 2D 內(nèi)容,這看起來(lái)有點(diǎn)奇怪。 當(dāng)涉及 Stage3D APIs 時(shí),很多人認(rèn)為這些 APIs 是嚴(yán)格地限制在 3D 內(nèi)容中的。 實(shí)際上這是名稱造成的誤解:如果它叫做 Stage3D ,那么你怎么可以使用它創(chuàng)建 2D 元素呢?下圖說(shuō)明了關(guān)于使用 drawTriangles API 繪制 MovieClip 能力的問(wèn)題(參見(jiàn)圖3)。
圖3. 可以使用 drawTriangles API 創(chuàng)建2D影片剪輯嗎?GPU 具有較高的效率并能快速地繪制三角形。 通過(guò)使用 drawTriangles API ,你可以繪制兩個(gè)三角形,然后選取一種紋理并且使用UV映射將它應(yīng)用到三角形中。 這樣可以創(chuàng)建了一個(gè)帶有紋理的四邊形,它代表一個(gè) sprite 。 通過(guò)更新每一個(gè)幀上的三角形的紋理,最后的結(jié)果就是一個(gè) MovieClip 。
幸好我們沒(méi)有必要通過(guò)這些細(xì)節(jié)使用 Starling 。 你只需要提供幀數(shù),將它們提供給一個(gè) Starling MovieClip ,這就是所有需要做的(參見(jiàn)圖4)。
圖4. 使用 drawTriangles API 和一個(gè)帶有紋理的四邊形,你可以創(chuàng)建一個(gè)2D圖形為了更好地了解 Starling 如何降低復(fù)雜性,檢查你必須寫(xiě)入的代碼以便于用低級(jí)的 Stage3D APIs 顯示簡(jiǎn)單的帶有紋理的四邊形。
1 // create the vertices2 var vertices:Vector.<Number> = Vector.<Number>([
3 -0.5,-0.5,0, 0, 0, // x, y, z, u, v
4 -0.5, 0.5, 0, 0, 1,
5 0.5, 0.5, 0, 1, 1,
6 0.5, -0.5, 0, 1, 0]);
7
8 // create the buffer to upload the vertices
9 var vertexbuffer:VertexBuffer3D = context3D.createVertexBuffer(4, 5);
10
11 // upload the vertices
12 vertexbuffer.uploadFromVector(vertices, 0, 4);
13
14 // create the buffer to upload the indices
15 var indexbuffer:IndexBuffer3D = context3D.createIndexBuffer(6);
16
17 // upload the indices
18 indexbuffer.uploadFromVector (Vector.<uint>([0, 1, 2, 2, 3, 0]), 0, 6);
19
20 // create the bitmap texture
21 var bitmap:Bitmap = new TextureBitmap();
22
23 // create the texture bitmap to upload the bitmap
24 var texture:Texture = context3D.createTexture(bitmap.bitmapData.width,
25
26 bitmap.bitmapData.height, Context3DTextureFormat.BGRA, false);
27
28 // upload the bitmap
29
30 texture.uploadFromBitmapData(bitmap.bitmapData);
31
32 // create the mini assembler
33 var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
34
35 // assemble the vertex shader
36 vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
37 "m44 op, va0, vc0\n" + // pos to clipspace
38 "mov v0, va1" // copy uv
39 );
40
41 // assemble the fragment shader
42 fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
43 "tex ft1, v0, fs0 <2d,linear, nomip>;\n" +
44 "mov oc, ft1"
45 );
46
47 // create the shader program
48 var program:Program3D = context3D.createProgram();
49
50 // upload the vertex and fragment shaders
51 program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
52
53 // clear the buffer
54 context3D.clear ( 1, 1, 1, 1 );
55
56 // set the vertex buffer
57 context3D.setVertexBufferAt(0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
58 context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
59
60 // set the texture
61 context3D.setTextureAt( 0, texture );
62
63 // set the shaders program
64 context3D.setProgram( program );
65
66 // create a 3D matrix
67 var m:Matrix3D = new Matrix3D();
68
69 // apply rotation to the matrix to rotate vertices along the Z axis
70 m.appendRotation(getTimer()/50, Vector3D.Z_AXIS);
71
72 // set the program constants (matrix here)
73 context3D.setProgramConstantsFromMatrix(Co
74 ntext3DProgramType.VERTEX, 0, m, true);
75
76 // draw the triangles
77 context3D.drawTriangles( indexBuffer);
78
79 // present the pixels to the screen
80 context3D.present();
上述范例中的代碼創(chuàng)建了一個(gè)正方形的2D實(shí)例(參見(jiàn)圖5):
圖5. 使用drawTriangles API 和一個(gè)帶有紋理的四邊形創(chuàng)建一個(gè)2D對(duì)象的結(jié)果上述范例中所示的代碼無(wú)疑是非常復(fù)雜的。 那是訪問(wèn)低級(jí) APIs 需要付出的代價(jià)。 從另一方面說(shuō),你可以控制很多方面,但是它需要大量的代碼來(lái)設(shè)置一切。
通過(guò) Starling,你可以編寫(xiě)如下代碼替換上述代碼:
1 // create a Texture object out of an embedded bitmap2 var texture:Texture = Texture.fromBitmap ( new embeddedBitmap() );
3
4 // create an Image object our of the Texture
5 var image:Image = new Image(texture);
6
7 // set the properties
8 quad.pivotX = 50;
9 quad.pivotY = 50;
10 quad.x = 300;
11 quad.y = 150;
12 quad.rotation = Math.PI/4;
13
14 // display it
15 addChild(quad);
作為一個(gè)熟知如何使用 Flash APIs 的 ActionScript 3 開(kāi)發(fā)人員,你可以立即使用這些已暴露的 APIs 開(kāi)始工作,與此同時(shí) Stage3D APIs 的所有復(fù)雜部分都可以在后臺(tái)進(jìn)行處理。
如果你使用重繪區(qū)域(redraw regions)功能進(jìn)行試驗(yàn),在 Starling 將在 Stage3D 上,而不是在預(yù)期的傳統(tǒng)顯示列表上渲染一切。 如下的截圖說(shuō)明了這種行為。 該四邊形在每一幀上旋轉(zhuǎn),重繪區(qū)域(redraw regions)只顯示FTP計(jì)數(shù)器,而不是 Stage3D 的內(nèi)容(參見(jiàn)圖6):
圖6. 使用Stage3D渲染內(nèi)容的范例需要記住在使用 Stage 3D 構(gòu)架時(shí),通過(guò) GPU 可以完全地渲染并合成相應(yīng)的內(nèi)容。 因此,在 GPU 上運(yùn)行的用于顯示列表的重繪區(qū)域(redraw regions)功能不能使用。
分層限制
當(dāng)你使用 Starling(以及Stage 3D)時(shí),記住開(kāi)發(fā)內(nèi)容有一個(gè)限制。 正如之前所述,Stage3D 完全是嵌入在 Flash Player 中的全新的渲染構(gòu)架。 GPU 表層放置在顯示列表之下,這意味著任何在顯示列表中運(yùn)行的內(nèi)容將放置到 Stage3D 內(nèi)容之上。 在編寫(xiě)這篇文章時(shí),運(yùn)行在顯示列表里的內(nèi)容仍然不能放置在 Stage3D 分層之下(參見(jiàn)圖7)。
圖7. 使用Stage3D渲染內(nèi)容的堆疊順序此外,還要注意 Stage3D 對(duì)象不是透明的。 如果這是可能的,那么你可以使用?Stage Video 技術(shù)(Flash Player 10.2引入的功能)播放視頻,同時(shí)可以用通過(guò) Stage3D 渲染的內(nèi)容覆蓋視頻。 希望未來(lái)的 Flash Player 版本支持這一功能。
設(shè)置項(xiàng)目
你可以訪問(wèn)官方的?Github頁(yè)面下載Starling。 此外,你可能發(fā)現(xiàn)訪問(wèn)?Starling 網(wǎng)站也會(huì)受益匪淺。
Starling根據(jù) Simplified BSD許可獲得授權(quán),所以你可以在任何類型的商業(yè)或者非商業(yè)項(xiàng)目上使用Starling。 如果你需要更多的信息,你可以聯(lián)系?Starling 框架團(tuán)隊(duì)。
在你下載Starling之后,你可以像引用其它AS3庫(kù)一樣引用Starling庫(kù)。 為了使用Flash Player 11 beta的新版本,你必須把SWF版本13作為目標(biāo),這是通過(guò)將額外的編譯器參數(shù)即-swf-version=13?傳遞給Flex編譯器實(shí)現(xiàn)的。如果你正在使用 Adobe Flex SDK,那么請(qǐng)按照如下步驟操作:
設(shè)置場(chǎng)景
在你已經(jīng)準(zhǔn)備好了你的開(kāi)發(fā)環(huán)境之后,你就可以深入研究相應(yīng)的代碼,并且看看你如何能夠充分利用這一框架。 使用 Starling 非常簡(jiǎn)單,你只需創(chuàng)建一個(gè)?Starling?對(duì)象并添加到你的主類即可。 在本文中,當(dāng)涉及到諸如?MovieClip,?Sprite?以及其它對(duì)象時(shí),我所指的都是 Starling APIs,而不是來(lái)源于 Flash Player 的本地對(duì)象。
首先,Starling構(gòu)造器(constructor)需要多重參數(shù)。 下面是簽名:
1 public function Starling(rootClass:Class, stage:flash.display.Stage, viewPort:Rectangle=null, stage3D:Stage3D=null, renderMode:String="auto")?
事實(shí)上,只有前面3個(gè)經(jīng)常使用。 相關(guān)的?rootClass?參數(shù)需要一個(gè)至擴(kuò)展?starling.display.Sprite?的類的引用,而第二個(gè)參數(shù)是我們的 stage,然后是一個(gè) Stage3D 對(duì)象:
1 package2 {
3 import flash.display.Sprite;
4 import flash.display.StageAlign;
5 import flash.display.StageScaleMode;
6 import starling.core.Starling;
7
8 [SWF(width="1280", height="752", frameRate="60", backgroundColor="#002143")]
9 public class Startup extends Sprite
10 {
11 private var mStarling:Starling;
12
13 public function Startup()
14 {
15 // stats class for fps
16 addChild ( new Stats() );
17
18 stage.align = StageAlign.TOP_LEFT;
19 stage.scaleMode = StageScaleMode.NO_SCALE;
20
21 // create our Starling instance
22 mStarling = new Starling(Game, stage);
23
24 // set anti-aliasing (higher is better quality but slower performance)
25 mStarling.antiAliasing = 1;
26
27 // start it!
28 mStarling.start();
29 }
30 }
31 }
?
在下面的代碼中,Game類在被添加到 Stage 時(shí)可以創(chuàng)建一個(gè)簡(jiǎn)單的四邊形:
1 package2 {
3 import starling.display.Quad;
4 import starling.display.Sprite;
5 import starling.events.Event;
6
7 public class Game extends Sprite
8 {
9 private var q:Quad;
10
11 public function Game()
12 {
13 addEventListener(Event.ADDED_TO_STAGE, onAdded);
14 }
15
16 private function onAdded ( e:Event ):void
17 {
18 q = new Quad(200, 200);
19 q.setVertexColor(0, 0x000000);
20 q.setVertexColor(1, 0xAA0000);
21 q.setVertexColor(2, 0x00FF00);
22 q.setVertexColor(3, 0x0000FF);
23 addChild ( q );
24 }
25 }
26 }
上述代碼將一個(gè)偵聽(tīng)器添加到 Event.ADDED_TO_STAGE 事件中,并在事件處理程序中對(duì)應(yīng)用程序進(jìn)行初始化。 這樣你就可以安全地訪問(wèn) Stage。
注意:?關(guān)注一下這個(gè)微妙的細(xì)節(jié):上面描述的 Game 類從 starling.display 程序包中,而不是從 flash.display 程序包中擴(kuò)展了 Sprite 類。必須檢查你的導(dǎo)入語(yǔ)句并確保你不是使用本地 API 來(lái)替代 Starling API。
正如在 Flash 中所預(yù)期的,Starling 中的對(duì)象有一個(gè)默認(rèn)的位置0,0。因此添加幾行命令使四邊形位于 Stage 的中央:
1 q.x = stage.stageWidth - q.width >> 1;2 q.y = stage.stageHeight - q.height >> 1;
現(xiàn)在,測(cè)試一下項(xiàng)目以便于觀察相應(yīng)的結(jié)果(參見(jiàn)圖8):
圖8. 四邊形位于Stage的中央注意鋸齒消除功能(anti-aliasing)值允許你設(shè)置鋸齒消除功能所需的類型。 一般來(lái)說(shuō),值為1就基本上可以接受,但是你可以選擇其它值。 該框架支持的鋸齒消除功能(anti-aliasing)值的變化范圍是0到16,但是,下面的列表給出了最常用的值:
- 0: 無(wú)鋸齒消除(anti-aliasing)。
- 2: 最低程度的鋸齒消除(anti-aliasing)。
- 4: 高質(zhì)量的鋸齒消除(anti-aliasing)。
- 16: 極高質(zhì)量的鋸齒消除(anti-aliasing)。
你很少需要用到超過(guò)2的設(shè)置,尤其是對(duì)2D內(nèi)容。 然而,根據(jù)你的項(xiàng)目要求,你需要針對(duì)具體情況作出相應(yīng)的決定。 在圖9中,比較一下兩個(gè)截圖,觀察兩個(gè)鋸齒消除(anti-aliasing)值(1和4)之間的細(xì)微差別。
圖9. 比較一下鋸齒消除(anti-aliasing)值為1(左)和4(右)之間的視覺(jué)差別試驗(yàn)一下使用2以上的值為你的項(xiàng)目設(shè)置所需的質(zhì)量。 當(dāng)然,選擇較高的值會(huì)影響性能。 注意 Stage3D 不會(huì)受到 SWF 文件的 Stage 質(zhì)量影響。
下面給出能夠與 Starling 對(duì)象一起使用的其它 API 的描述:
- enableErrorChecking:?允許你啟用或者禁止啟用錯(cuò)誤檢查。 指定是否將渲染器遇到的問(wèn)題報(bào)告給應(yīng)用程序。 當(dāng) enableErrorChecking 設(shè)置為ture時(shí),Starling 內(nèi)部調(diào)用的 clear() 和 drawTriangles() 方法是同步的并可以拋出錯(cuò)誤。 當(dāng) enableErrorChecking 設(shè)置為 false 時(shí),clear() 和 drawTriangles() 方法是異步的且不報(bào)告錯(cuò)誤。 啟用錯(cuò)誤檢查將會(huì)減弱渲染性能。 只有當(dāng)調(diào)試項(xiàng)目時(shí)啟用錯(cuò)誤檢查功能,而在部署最終版本前禁止啟用該功能。
- isStarted:指示是否調(diào)用了 start。
- juggler:?juggler 是一個(gè)簡(jiǎn)單對(duì)象。 它僅保存了一列執(zhí)行 IAnimatable 的對(duì)象,并且在被要求這樣做的情形下提前了它們的時(shí)間(通過(guò)調(diào)用它自己的 advanceTime:方法)。 當(dāng)一個(gè)動(dòng)畫(huà)完成時(shí),它將會(huì)將其拋棄。
- start:?開(kāi)始渲染和進(jìn)行事件處理。
- stop:?停止渲染和進(jìn)行事件處理。 當(dāng)游戲進(jìn)入后臺(tái)運(yùn)行狀態(tài)以節(jié)約資源時(shí),使用這個(gè)方法可以停止渲染。
- dispose:?當(dāng)你希望處理當(dāng)前 GPU 內(nèi)存上已渲染的全部?jī)?nèi)容時(shí),調(diào)用這個(gè)方法。 該API能夠在其內(nèi)部處理了一切事務(wù)(例如著色程序(shader programs)、紋理和其它一切事務(wù))。
一旦創(chuàng)建了你的?Starling?對(duì)象,調(diào)試記錄會(huì)自動(dòng)地輸出,顯示關(guān)于渲染的信息。 在默認(rèn)情形下,當(dāng) SWF 文件正確地嵌入到頁(yè)面或者當(dāng)在獨(dú)立的 Flash Player 中進(jìn)行測(cè)試時(shí),Starling 會(huì)輸出如下代碼:
[Starling] Initialization complete.[Starling] Display Driver:OpenGL Vendor=NVIDIA Corporation Version=2.1 NVIDIA-7.2.9 Renderer=NVIDIA GeForce GT 330M OpenGL Engine GLSL=1.20 (Direct blitting)
當(dāng)然,特定的硬件細(xì)節(jié)將會(huì)隨著你的配置而變化。 上述信息表明已經(jīng)使用了 GPU 加速功能,因?yàn)樗?qū)動(dòng)版本的細(xì)節(jié)。 為了便于調(diào)試,你可能希望能夠強(qiáng)迫 Flash Player 內(nèi)部使用的軟件回退,以便了解當(dāng)你的內(nèi)容在軟件上運(yùn)行時(shí)它的表現(xiàn)如何。
添加如下的代碼以便于通知 Starling 你希望使用軟件回退功能(software rasterizer):
1 mStarling = new Starling(Game, stage, null, null, Context3DRenderMode.SOFTWARE);當(dāng)你使用軟件時(shí),輸出的信息會(huì)確認(rèn)你正在使用software 模式:
[Starling] Initialization complete.[Starling] Display Driver:Software (Direct blitting)
確保你也在 software 模式下測(cè)試了你的內(nèi)容,以便于更好地了解它在這種模式下的性能。 如果用戶的配置使用舊版本的驅(qū)動(dòng)(為了保持一致性,所有2009年之前的驅(qū)動(dòng)都包含于黑名單中),那么你的內(nèi)容可能回退到軟件。
在下一節(jié)中,當(dāng)你將你的 SWF 文件嵌入到頁(yè)面時(shí),你需要看一下 Stage3D 的要求。
Wmode 要求
你必須記住為了啟用Stage 3D 和 GPU 加速功能,在頁(yè)面中你必須使用?wmode=direct?作為嵌入模式。 如果你沒(méi)有指定任何值或者選擇除“direct”之外其它值,例如 “transparent”、“opaque” 或 “window”,則Stage 3D 將均不可用。相反,當(dāng)?requestContext3D?onStage3D被調(diào)用時(shí),你會(huì)得到一個(gè)運(yùn)行時(shí)異常的提示,告知你Context3D對(duì)象的創(chuàng)建失敗。
下圖列舉了一個(gè)運(yùn)行時(shí)異常的對(duì)話框:
圖10. 在 Context3D不可用的情形下,運(yùn)行時(shí)異常對(duì)話框如果你的應(yīng)用程序嵌入時(shí)使用了錯(cuò)誤的wmode,那么必須小心處理這種情形。 你需要通過(guò)顯示一條解釋這一問(wèn)題的信息以便給出合理的響應(yīng)。 幸運(yùn)的是,Starling為你自動(dòng)地處理了這一問(wèn)題并顯示如下信息:
圖11. 當(dāng)應(yīng)用程序沒(méi)有正確嵌入時(shí)顯示的警告信息Stage質(zhì)量
作為一個(gè)Flash開(kāi)發(fā)人員,stage質(zhì)量的概念對(duì)你來(lái)說(shuō)并不陌生。 記住當(dāng)使用 Stage3D以及作為結(jié)果的Starling時(shí),stage質(zhì)量不會(huì)影響相應(yīng)的性能。
漸進(jìn)的增強(qiáng)功能
當(dāng)GPU加速功能不起作用時(shí),Stage3D將回退到軟件中,并且將在內(nèi)部使用一個(gè)名稱為SwiftShader (Transgaming)的軟件回退引擎。 為了保證你的內(nèi)容在此種情形下運(yùn)行正常,你需要檢測(cè)什么時(shí)候你應(yīng)該在software模式下運(yùn)行,并且移除在software模式下可能會(huì)減慢運(yùn)行速度的潛在影響。
在2D內(nèi)容環(huán)境下,軟件回退功能能夠處理很多對(duì)象并提供良好的性能,但是,為了檢測(cè)這一點(diǎn),你仍然可以使用靜態(tài)的屬性環(huán)境從Starling對(duì)象中讀取Context3D對(duì)象:
// are we running hardware or software?var isHW:Boolean = Starling.context.driverInfo.toLowerCase().indexOf("software") == -1;
記住使用軟件回退功能設(shè)計(jì)你的內(nèi)容是一種很好的做法,它將提供一種漸進(jìn)式的體驗(yàn),從而確保在任何情形下都能獲得最佳體驗(yàn)效果
轉(zhuǎn)載于:https://www.cnblogs.com/klh5211314/p/3158674.html
總結(jié)
以上是生活随笔為你收集整理的Starling 2D框架简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿里云服务器挖矿脚本bioset攻击解决
- 下一篇: [转] 英语、计算机、互联网与全球化