顯示列表
starling和flash本地的顯示列表有一樣的規則,在沒有東西添加進stage之前,stage是null的。在本地flash中,為了能更安全的使用stage,我們通常使用一些Flash中的重要的事件,這些事件在starling中同樣可用:
• Event.ADDED : the object was added to a parent.
• Event.ADDED_TO_STAGE : the object was added to a parent that is connected to the stage, thus becoming
visible now.
• Event.REMOVED : the object was removed from a parent.
• Event.REMOVED_FROM_STAGE : the object was removed from a parent that is connected to the stage,
thus becoming invisible now.
在下面的例子中,我們會經常用到這些事件,就像在flash中一樣,這些事件可以用在初始化,組件銷毀等很多重要的地方。
下面是DisplayObject類聲明的部分方法列表:
• removeFromParent : Removes the object from its parent, if it has one.
• getTransformationMatrixToSpace : Creates a matrix that represents the transformation from the local
coordinate system to another.
• getBounds : Returns a rectangle that completely encloses the object as it appears in another coordinate system.
• hitTestPoint : Returns the object that is found topmost on a point in local coordinates, or nil if the test fails.
• globalToLocal : Transforms a point from global (stage) coordinates to the local coordinate system.
• localToGlobal : Transforms a point from the local coordinate system to global (stage) coordinates.
下面是DisplayObject類聲明的部分屬性,讓人高興的是大部分的屬性都和flash本事的display一樣,並且還有一些拓展的屬性,比如用來動態改變一個DisplayObject類注冊點的pivotX和pivotY,如下:
• transformationMatrix : The transformation matrix of the object relative to its parent.
• bounds : The bounds of the object relative to the local coordinates of the parent.
• width : The width of the object in points.
• height : The height of the object in points.
• root : The topmost object in the display tree the object is part of.
• x : The x coordinate of the object relative to the local coordinates of the parent.
• y : The y coordinate of the object relative to the local coordinates of the parent.
• pivotX : The x coordinate of the object’s origin in its own coordinate space (default: 0).
• pivotY : The y coordinate of the object’s origin in its own coordinate space (default: 0).
• scaleX : The horizontal scale factor. “1” means no scale, negative values flip the object.
• scaleY : The vertical scale factor. “1” means no scale, negative values flip the object.
• rotation : The rotation of the object in radians. (In Sparrow, all angles are measured in radians.)
• alpha : The opacity of the object.
• visible : The visibility of the object. An invisible object will be untouchable.
• touchable : Indicates if this object (and its children) will receive touch events.
• parent : The display object container that contains this display object.
• stage : The stage the display object is connected to, or null if it is not connected to a stage.
就像在flash本地的api中一樣,Sprite是你可以使用的最輕量級的容器。Sprite繼承自DisplayObjectContainer, 而DisplayObjectContainer繼承自DisplayObject,我們的例子中的Game都是集成字Sprite的,是一個DisplayObjectContainer。
下面是DisplayObjectContainer的一部分api:
• addChild : Adds a child to the container. It will be at the topmost position.
• addChildAt : Adds a child to the container at a certain index.
• dispose : Removes the GPU buffers and all the listeners registered to the o
• removeFromParent : Removes the child from its parent.
• removeChild : Removes a child from the container. If the object is not a ch
• removeChildAt : Removes a child at a certain index. Children above the c
• removeChildren : Removes all children from the container.
• getChildAt : Returns a child object at a certain index.
• getChildByName : Returns a child object with a certain name (non-recursi
• getChildIndex : Returns the index of a child within the container.
• setChildIndex : Changes the index of the specified child.
• swapChildren : Swaps the indexes of two children.
• swapChildrenAt : Swaps the indexes of two children.
• contains : Determines if a certain object is a child of the container (recursively).
一旦你進入了stage之后你就可以使用絕大部分DisplayObjectContainer的api,當然了你可以為stage設置一個顏色。Starling默認會采用SWF的背景色,你可以通過設置下面的SWF標簽來實現:
[SWF(width="1280", height="752", frameRate="60", backgroundColor="#990000")]
你也可以通過stage對象來手工設置一個顏色,stage對象可以通過任意一個添加到顯示列表中的DisplayObject對象中獲得:
1 package
2 {
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 // set the background color to blue
19 stage.color = 0x002143;
20
21 q = new Quad(200, 200);
22 q.setVertexColor(0, 0x000000);
23 q.setVertexColor(1, 0xAA0000);
24 q.setVertexColor(2, 0x00FF00);
25 q.setVertexColor(3, 0x0000FF);
26 addChild ( q );
27 }
28 }
29 }
現在我們還沒有使用任何texture(紋理),我們主要是用兩個三角形拼了一個Quad,並且每個頂點擁有不同的顏色(會自動篡改成gpu可識別的顏色)。
當然了你可以調用quad的color屬性來實現純色的quad:
1 package
2 {
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.color = 0x00FF00;
20 q.x = stage.stageWidth - q.width >> 1;
21 q.y = stage.stageHeight - q.height >> 1;
22 addChild ( q );
23 }
24 }
25 }
效果如下:
下面我們來使用Event.ENTER_FRAME事件修改quad的顏色來實現一個簡單緩動特效:
1 package
2 {
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 private var r:Number = 0;
12 private var g:Number = 0;
13 private var b:Number = 0;
14
15 private var rDest:Number;
16 private var gDest:Number;
17 private var bDest:Number;
18
19 public function Game()
20 {
21 addEventListener(Event.ADDED_TO_STAGE, onAdded);
22 }
23
24 private function onAdded ( e:Event ):void
25 {
26 resetColors();
27
28 q = new Quad(200, 200);
29 q.x = stage.stageWidth - q.width >> 1;
30 q.y = stage.stageHeight - q.height >> 1;
31 addChild ( q );
32
33 s.addEventListener(Event.ENTER_FRAME, onFrame);
34 }
35
36 private function onFrame (e:Event):void
37 {
38 r -= (r - rDest) * .01;
39 g -= (g - gDest) * .01;
40 b -= (b - bDest) * .01;
41
42 var color:uint = r << 16 | g << 8 | b;
43 q.color = color;
44
45 // when reaching the color, pick another one
46 if ( Math.abs( r - rDest) < 1 && Math.abs( g - gDest) < 1 && Math.abs( b - bDest) )
47 resetColors();
48 }
49
50 private function resetColors():void
51 {
52 rDest = Math.random()*255;
53 gDest = Math.random()*255;
54 bDest = Math.random()*255;
55 }
56 }
57 }
我們可以使用rotation這個屬性來旋轉quad,但是注意,在Starling中使用的是弧度而Flash Player中使用的是角度。這樣做是為了保持Sparrow和Starling的一致性(Sparrow是Strarling的姊妹篇,同一作者)。如果你想使用度數來實現旋轉的話,只要使用 starling.utils.deg2rad 這個放來來轉換一下就可以了:
sprite.rotation = deg2rad(Math.random()*360);
我們可以在運行時設置DisplayObject的pivotX和pivotY屬性來改變注冊點:
q.pivotX = q.width >> 1;
q.pivotY = q.height >> 1;
對於as3開發者來說使用starling會感覺很自然,下面的這個例子,一個quad和一個textfield被添加到一個sprite中,並且可以作為一個組合一起移動,這和我們使用本地的顯示列表時一樣的:
package
{
import starling.display.DisplayObject;
import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;
import starling.text.TextField;
public class Game extends Sprite
{
private var q:Quad;
private var s:Sprite;
private var r:Number = 0;
private var g:Number = 0;
private var b:Number = 0;
private var rDest:Number;
private var gDest:Number;
private var bDest:Number;
public function Game()
{
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded ( e:Event ):void
{
resetColors();
q = new Quad(200, 200);
s = new Sprite();
var legend:TextField = new TextField(100, 20, "Hello Starling!", "Arial", 14, 0xFFFFFF);
s.addChild(q);
s.addChild(legend);
s.pivotX = s.width >> 1;
s.pivotY = s.height >> 1;
s.x = (stage.stageWidth - s.width >> 1 ) + (s.width >> 1);
s.y = (stage.stageHeight - s.height >> 1) + (s.height >> 1);
addChild(s);
s.addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame (e:Event):void
{
r -= (r - rDest) * .01;
g -= (g - gDest) * .01;
b -= (b - bDest) * .01;
var color:uint = r << 16 | g << 8 | b;
q.color = color;
// when reaching the color, pick another one
if ( Math.abs( r - rDest) < 1 && Math.abs( g - gDest) < 1 && Math.abs( b - bDest) )
resetColors();
(e.currentTarget as DisplayObject).rotation += .01;
}
private function resetColors():void
{
rDest = Math.random()*255;
gDest = Math.random()*255;
bDest = Math.random()*255;
}
}
}
我們現在圍繞着注冊點旋轉這個包含quad和textfield的sprite:
我們的代碼現在看起來開始有些雜亂了,那么我們提取一些代碼來組建一個CustomSprite類,用這個類封裝顏色的變化、quad和textfield。代碼如下:
1 package
2 {
3 import starling.display.Quad;
4 import starling.display.Sprite;
5 import starling.events.Event;
6 import starling.text.TextField;
7 public class CustomSprite extends Sprite
8 {
9 private var quad:Quad;
10 private var legend:TextField;
11
12 private var quadWidth:uint;
13 private var quadHeight:uint;
14
15 private var r:Number = 0;
16 private var g:Number = 0;
17 private var b:Number = 0;
18
19 private var rDest:Number;
20 private var gDest:Number;
21 private var bDest:Number;
22
23 public function CustomSprite(width:Number, height:Number, colo
24 {
25 // reset the destination color component
26 resetColors();
27
28 // set the width and height
29 quadWidth = width;
30 quadHeight = height;
31
32 // when added to stage, activate it
33 addEventListener(Event.ADDED_TO_STAGE, activate);
34 }
35
36 private function activate(e:Event):void
37 {
38 // create a quad of the specified width
39 quad = new Quad(quadWidth, quadHeight);
40 // add the legend
41 legend = new TextField(100, 20, "Hello Starling!", "Arial
42
43 // add the children
44 addChild(quad);
45 addChild(legend);
46
47 // change the registration point
48 pivotX = width >> 1;
49 pivotY = height >> 1;
50 }
51
52 private function resetColors():void
53 {
54 // pick random color components
55 rDest = Math.random()*255;
56 gDest = Math.random()*255;
57 bDest = Math.random()*255;
58 }
59
60 /**
61 * Updates the internal behavior
62 *
63 */
64 public function update ():void
65 {
66 // easing on the components
67 r -= (r - rDest) * .01;
68 g -= (g - gDest) * .01;
69 b -= (b - bDest) * .01;
70
71 // assemble the color
72 var color:uint = r << 16 | g << 8 | b;
73 quad.color = color;
74
75 // when reaching the color, pick another one
76 if ( Math.abs( r - rDest) < 1 && Math.abs( g - gDest) < 1 && Math.abs( b - bDest) )
77 resetColors();
78
79 // rotate it!
80 //rotation += .01;
81 }
82 }
83 }
這樣的話,我們的Game類變為這樣:
1 package
2 {
3 import starling.display.Sprite;
4 import starling.events.Event;
5
6 public class Game extends Sprite
7 {
8 private var customSprite:CustomSprite;
9
10 public function Game()
11 {
12 addEventListener(Event.ADDED_TO_STAGE, onAdded);
13 }
14
15 private function onAdded ( e:Event ):void
16 {
17 // create the custom sprite
18 customSprite = new CustomSprite(200, 200);
19
20 // positions it by default in the center of the stage
21 // we add half width because of the registration point of the custom sprite (middle)
22 customSprite.x = (stage.stageWidth - customSprite.width >> 1 ) + (customSprite.width >> 1);
23 customSprite.y = (stage.stageHeight - customSprite.height >> 1) + (customSprite.height >> 1);
24
25 // show it
26 addChild(customSprite);
27
28 // need to comment this one ? ;)
29 stage.addEventListener(Event.ENTER_FRAME, onFrame);
30 }
31
32 private function onFrame (e:Event):void
33 {
34 // we update our custom sprite
35 customSprite.update();
36 }
37 }
38 }
注意,我們使用CustomSprite中的update方法來在主sprite中進行循環操作(即一個組建提取的概念), 通過使用這樣的方法,我們增加一個全局暫停的方法將會變的很簡單。
讓我們來給我們的小測試增加一些功能,讓我們的quad跟隨鼠標移動。在下面的代碼中,我們新增了一小段代碼來實現這個功能:
1 package
2 {
3 import flash.geom.Point;
4
5 import starling.display.Sprite;
6 import starling.events.Event;
7 import starling.events.Touch;
8 import starling.events.TouchEvent;
9
10 public class Game extends Sprite
11 {
12 private var customSprite:CustomSprite;
13 private var mouseX:Number = 0;
14 private var mouseY:Number = 0;
15
16 public function Game()
17 {
18 addEventListener(Event.ADDED_TO_STAGE, onAdded);
19 }
20
21 private function onAdded ( e:Event ):void
22 {
23 // create the custom sprite
24 customSprite = new CustomSprite(200, 200);
25
26 // positions it by default in the center of the stage
27 // we add half width because of the registration point of the custom sprite (middle)
28 customSprite.x = (stage.stageWidth - customSprite.width >> 1 ) + (customSprite.width >> 1);
29 customSprite.y = (stage.stageHeight - customSprite.height >> 1) + (customSprite.height >> 1);
30
31 // show it
32 addChild(customSprite);
33
34 // we listen to the mouse movement on the stage
35 stage.addEventListener(TouchEvent.TOUCH, onTouch);
36 // need to comment this one ? ;)
37 stage.addEventListener(Event.ENTER_FRAME, onFrame);
38 }
39
40 private function onFrame (e:Event):void
41 {
42 // easing on the custom sprite position
43 customSprite.x -= ( customSprite.x - mouseX ) * .1;
44 customSprite.y -= ( customSprite.y - mouseY ) * .1;
45
46 // we update our custom sprite
47 customSprite.update();
48 }
49
50 private function onTouch (e:TouchEvent):void
51 {
52 // get the mouse location related to the stage
53 var touch:Touch = e.getTouch(stage);
54 var pos:Point = touch.getLocation(stage);
55 // store the mouse coordinates
56 mouseX = pos.x;
57 mouseY = pos.y;
58 }
59 }
60 }
這里需要注意的是,我們沒有使用任何鼠標事件,實際上在starling中沒有鼠標的概念。下面將會很快提到這里(event模塊)。
通過監聽 TouchEvent.TOUCH事件,我們可以處理任何鼠標/手指的運動(starling的設計其實目標很明確就是照着平板去的,讓你的程序技能在pc有很好的交互,到了平板上依舊ok,不用重復編碼)。比如典型的MousEvent.MOUSE_MOVE事件。上例中,我們使用TouchEvent監聽來獲得鼠標位置,並存貯到變量中,然后在frame監聽中使用簡單的賦值來移動customSprite。
就像前面提到的,Starling不只是可以簡化gpu編程,並且簡化了對象的釋放操作。假設我們想要在點擊的quad的時候將它從場景中移除,代碼如下:
1 package
2 {
3 import flash.geom.Point;
4
5 import starling.display.DisplayObject;
6 import starling.display.Sprite;
7 import starling.events.Event;
8 import starling.events.Touch;
9 import starling.events.TouchEvent;
10 import starling.events.TouchPhase;
11
12 public class Game extends Sprite
13 {
14 private var customSprite:CustomSprite;
15 private var mouseX:Number = 0;
16 private var mouseY:Number = 0;
17
18 public function Game()
19 {
20 addEventListener(Event.ADDED_TO_STAGE, onAdded);
21 }
22
23 private function onAdded ( e:Event ):void
24 {
25 // create the custom sprite
26 customSprite = new CustomSprite(200, 200);
27
28 // positions it by default in the center of the stage
29 // we add half width because of the registration point of the custom sprite (middle)
30 customSprite.x = (stage.stageWidth - customSprite.width >> 1 ) + (customSprite.width >> 1);
31 customSprite.y = (stage.stageHeight - customSprite.height >> 1) + (customSprite.height >> 1);
32
33 // show it
34 addChild(customSprite);
35
36 // we listen to the mouse movement on the stage
37 stage.addEventListener(TouchEvent.TOUCH, onTouch);
38 // need to comment this one ? ;)
39 stage.addEventListener(Event.ENTER_FRAME, onFrame);
40 // when the sprite is touched
41 customSprite.addEventListener(TouchEvent.TOUCH, onTouchedSprite);
42 }
43
44 private function onFrame (e:Event):void
45 {
46 // easing on the custom sprite position
47 customSprite.x -= ( customSprite.x - mouseX ) * .1;
48 customSprite.y -= ( customSprite.y - mouseY ) * .1;
49
50 // we update our custom sprite
51 customSprite.update();
52 }
53
54 private function onTouch (e:TouchEvent):void
55 {
56 // get the mouse location related to the stage
57 var touch:Touch = e.getTouch(stage);
58 var pos:Point = touch.getLocation(stage);
59
60 // store the mouse coordinates
61 mouseX = pos.x;
62 mouseY = pos.y;
63 }
64
65 private function onTouchedSprite(e:TouchEvent):void
66 {
67 // get the touch points (can be multiple because of multitouch)
68 var touch:Touch = e.getTouch(stage);
69 var clicked:DisplayObject = e.currentTarget as DisplayObject;
70
71 // detect the click/release phase
72 if ( touch.phase == TouchPhase.ENDED )
73 {
74 // remove the clicked object
75 removeChild(clicked);
76 }
77 }
78 }
79 }
注意在這里我們移除了子對象但是並沒有移除Event.ENTER_FRAME監聽,我們可以使用hasEventListener來看看customSprite的監聽是否被移除掉了:
1 private function onTouchedSprite(e:TouchEvent):void
2 {
3 // get the touch points (can be multiple because of multitouch)
4 var touch:Touch = e.getTouch(stage);
5 var clicked:DisplayObject = e.currentTarget as DisplayObject;
6
7 // detect the click/release phase
8 if ( touch.phase == TouchPhase.ENDED )
9 {
10 // remove the clicked object
11 removeChild(clicked);
12
13 // outputs : true
14 trace ( clicked.hasEventListener(e.type) );
15 }
16 }
為了能安全的移除子對象,你可以啟用removeChild方法中的第二個參數dispose。它可以自動的將被刪除的子對象的所有監聽移除:
1 private function onTouchedSprite(e:TouchEvent):void
2 {
3 // get the touch points (can be multiple because of multitouch)
4 var touch:Touch = e.getTouch(stage);
5 var clicked:DisplayObject = e.currentTarget as DisplayObject;
6
7 // detect the click/release phase
8 if ( touch.phase == TouchPhase.ENDED )
9 {
10 // remove and dispose all the listeners
11 removeChild(clicked, true);
12
13 // outputs : false
14 trace ( clicked.hasEventListener(e.type) );
15 }
16 }
如果子對象也是一個容器,還包含着其他子對象,那么這些所有的對象也都將被移除掉(是不是非常方便!)。其他用來移除對象的api也可以使用dispose參數,比如:removeChildren或者removeChildAt。這里需要注意的是dispose操作也會清除掉gpu針對該對象的緩沖,但是該對象的texture不會被清除(為了復用嘛,並且starling中的texture和本地對象中的bitmapdata類似)。你可以通過調用Texture/TextureAtlas的dispose方法來釋放texture。
你也可以通過調用任何DisplayObject對象的dispose方法來移除對象的所有監聽:
1 clicked.dispose()
下一遍 事件模型