Posted on 1 Comment

產生TextureAtlas素材的方式

方法一、使用TexturePacker

軟體官網:http://www.codeandweb.com/texturepacker
範例圖檔下載:woman
螢幕快照 2014-02-12 下午2.39.46
首先,要先把圖檔整理成多個png圖檔,若是連續的圖檔,則需要在後面加0001~~XXXX之類的數字,固定是四個數字。若是單一圖檔則可以不用加。到時在取圖檔時,例如本範例是woman0001~woman0028,則只需要用woman當前置詞就可以取出全部的動態圖檔了

var woman:MovieClip = new MovieClip(atlas.getTextures("woman"),30);

而在操作TexturePacker時,最重要的是Data Format要選擇Sparrow/Starling。

索取TexturePacker免費序號

免費序號申請單:http://www.codeandweb.com/request-free-license

首先要說一下這套軟體的開發者真的很用心,當時看到有Blog說如果是有部落格的開發者,可以寫信去申請免費序號。我寫了申請表後,他回信了這封信給我:

Hi Claire,

Here’s your license key for TexturePacker:
TP-xxxxxxxxxxxxxxxx
I’ve added a license for PhysicsEditor (see file attached) in case you might want to try it too 😉
Nice blog! I would be happy to get a (short) blog post in return.
In case you do a tutorial post about my tools I can link back to your blog from the tutorials section on my page. That might give you some more visitors on your page!

Kind regards
Kerstin on behalf of Andreas

因為還有我的名字,我就回信問他中文的部落格可以嗎?結果居然真的是人工回信的耶(本以為都是自動發信),而且回信速度很快,真的很用心在經營他的產品。

最酷的是,後來因為我在寫Starling前面的教程,所以遲遲未寫關於TexturePacker的介紹文,他居然隔了一段時間又寄了封信給我:

Dear Claire,

I sent you a blogger license for TexturePacker some time ago,
and I am curious how you like the program.
Did you get it running successfully? Or do you need some assistance?
I would be happy to get a (short) blog post in return.

Kind regards
Kerstin on behalf of Andreas

還會關心我們的使用狀況耶!真的是超用心的開發者~

方法二、使用Flash CC

Flash CC和之前的版本比起來,就是它終於支援可以把元件庫的東西匯出了。

只需要在元件庫按右鍵,就可以選擇是要將其匯出成連續png或者是直接匯出成Sprite Sheet,並可同時選擇多個圖檔然後一起匯在同一個Spite Sheet裡。相關操作教學請見此:Sprite Sheets in Flash Professional CS6
螢幕快照 2014-02-12 下午2.47.52
螢幕快照 2014-02-12 下午2.49.40

Posted on

Starling的Display Objects介紹(五)

上一篇:Starling的Display Objects介紹(四) 下一篇:產生TextureAtlas素材的方式 這篇會介紹starling.display.MovieClip以及starling.animation.Juggler

MovieClip物件介紹

官方手冊在此:http://doc.starling-framework.org/core/starling/display/MovieClip.html 在Starling裡面的MovieClip和原生的MovieClip差異蠻大的,列舉說明如下:

  1. 每一個MovieClip都可設定不同的fps(需在new MovieClip時指定):這是因為一般來說在Starling裡的frameRate都會設定的很高,但動畫可能不會有這麼多圖片可跑,因此每一個MovieClip
  2. 由一連串的連續圖檔組成:MovieClip是由一連串的動畫圖片(TextureAtlas)輪流播放。
  3. MovieClip裡面無法有任何物件:在Starling裡的MovieClip並不是繼承DisplayObjectContainer,而是繼承於Image,因此無法addChild()。我們可以想像MovieClip在Starling裡是一個Image下面放著一張很大的png,然後有一個方型遮罩,不停的變換顯示的區塊。
  4. 動畫效果必需由Juggler驅動:在Starling裡所有的動畫物件都必需實作IAnimatable這個介面。而所有的動畫效果則統一由Juggler驅動(另外還包括Tween、DelayedCall)。
  5. 沒有frameLabel的概念(這部份有些Starling extension有加上去此功能)
  6. 當isComplete等於true時會停止動畫的播放

順便介紹兩個好用的method。第一個為setFrameDuration(),可以另外再設定某個影格的停留時間,影格數從0開始。而setFrameSound()可以設定播放到某影格時播放一個聲音檔。 連續圖檔的產生方式請見此文章:產生TextureAtlas的方式

Juggler物件介紹

官方手冊在此:http://doc.starling-framework.org/core/starling/animation/Juggler.html 介紹在此:http://grayliao.blogspot.tw/2011/11/starling-framework6jugglertweendelaycal.html

Juggle是個簡單的Class,用來控制動畫的進行。他負責管理經由add()加進來的實現IAnimatable介面的物件,然後當Juggler的advanceTime()被呼叫時,它會負責去呼叫這些IAnimatable的advanceTime(),讓動畫進行下去;而當某個IAnimatable到達complete狀態時,則會被Juggler踢出去。我們就只要負責每個frame去呼叫Juggler的advanceTime()就好。而Starling class裡有個預設的juggler,當Starling.current正在運行時,Starling.juggler在每個frame時會自動被呼叫advanceTime()。通常我們把遊戲裡的動畫加到Starling.juggler裡,而一些特殊的動畫,例如遊戲暫停時要撥放的動畫,再加到另一個我們新增的Juggler,然後每個frame去呼叫它的advanceTime()。這裡講的每個frame,我們是使用EnterFrameEvent.ENTER_FRAME,而不是Event.ENTER_FRAME,因為EnterFrameEvent可以取得passedTime,這個passTime是指跟上一次事件發生經過的時間,可以傳給Juggle的advanceTime()的第一個參數。用經過時間來播放動畫,這樣就不會受frame數不穩定而影響動畫撥放的時間。 而要實現IAnimatable介面,只要實作advanceTime()這個方法,並且要設定一個complete條件,當達到這個條件時將自己的isComplete設為true,這樣就可以自動被Juggle移除。

簡單的MovieClip範例

因為在Starling裡面,不論是MovieClip或是Button,都是正方形的。若要去判別透明,將透明的Touch事件設為無效,都需要去覆寫原有的類別裡的hitTest函數,以最原始的BitmapData的hitTest去判別是否為透明圖層。 這邊有一個已寫好的類別:AlphaMovieClip,已經寫好將透明圖層的所有hitTest事件設為無效。若有這個需求的人可以使用此類別來應用。下面是一個我寫的簡單應用範例,可點此下載原始檔:StarlingTest StarlingTest.as

package
{
	import flash.display.Sprite;

	import starling.core.Starling;

	[SWF(frameRate="60",Width="800",Height="600")]
	public class StarlingTest extends Sprite
	{
		public function StarlingTest()
		{
			var star:Starling = new Starling(Main, stage);
			star.start();
		}
	}
}

Main.as

package {
	import flash.display.Bitmap;

	import starling.core.Starling;
	import starling.display.MovieClip;
	import starling.display.Sprite;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;

	public class Main extends Sprite {
		[Embed(source = 'test.xml', mimeType = 'application/octet-stream')]
		private var AtlasXML:Class;

		[Embed(source = 'test.png')]
		private var AtlasTexture:Class;

		private var mc:AlphaMovieClip ;
		public function Main() {
			var bitmap:Bitmap = new AtlasTexture();
			var texture:Texture = Texture.fromBitmap(bitmap);
			var xml:XML = XML(new AtlasXML());
			var atlas:TextureAtlas = new TextureAtlas(texture, xml);

			mc = new AlphaMovieClip("run",atlas, bitmap.bitmapData,30);
			var m2:MovieClip = new MovieClip(atlas.getTextures("run"),30);
			m2.loop = false;
			m2.x = 200;
			mc.addEventListener(TouchEvent.TOUCH, touchEventHandler);
			addChild(mc);
			addChild(m2);

			Starling.juggler.add(mc);
			Starling.juggler.add(m2);
		}

		private function touchEventHandler(event:TouchEvent):void {
			var touch:Touch = event.getTouch(this);
			if(!touch ) return;
			if (touch.phase == TouchPhase.BEGAN){
				mc.pause();
			}else if(touch.phase == TouchPhase.ENDED){
				mc.play();
			}
		}
	}
}

AlphaMovieClip.as

package {
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	import starling.display.DisplayObject;
	import starling.display.MovieClip;
	import starling.textures.SubTexture;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;

	public class AlphaMovieClip extends MovieClip
	{
		private var m_TexturePrefix	:String				= "";
		private var m_TextureAtlas	:TextureAtlas		= null;
		private var m_Textures		:Vector.	= null;
		private var m_TextureInfo	:Dictionary			= null;
		private var m_BitmapData	:BitmapData			= null;

		public function AlphaMovieClip(texturePrefix:String, textureAtlas:TextureAtlas, bitmapData:BitmapData, fps:Number = 12)
		{
			m_TextureInfo = new Dictionary();

			var names:Vector. = textureAtlas.getNames(texturePrefix);
			for each (var name:String in names)
			{
				var textureInfo:Object = { };
				textureInfo.name = name;
				textureInfo.texture = textureAtlas.getTexture(name);
				m_TextureInfo[name] = textureInfo;
			}

			m_TexturePrefix = texturePrefix;
			m_TextureAtlas = textureAtlas;
			m_Textures = textureAtlas.getTextures(texturePrefix);
			m_BitmapData = bitmapData;

			super(m_Textures, fps);
		}

		override public function hitTest(localPoint:Point, forTouch:Boolean = false):DisplayObject
		{
			if (forTouch && visible && touchable)
			{
				if (getBounds(this).containsPoint(localPoint))
				{
					var texture:Texture = getFrameTexture(currentFrame);
					var subtexture:SubTexture = texture as SubTexture;
					var textureFrame:Rectangle = subtexture.frame;
					var clipping:Rectangle = subtexture.clipping;
					var frameBound:Rectangle = new Rectangle(
						Math.abs(textureFrame.x),
						Math.abs(textureFrame.y),
						clipping.width * m_TextureAtlas.texture.width,
						clipping.height * m_TextureAtlas.texture.height
					);
					clipping.x *= m_TextureAtlas.texture.width;
					clipping.y *= m_TextureAtlas.texture.height;

					var final_x:uint = (frameBound.containsPoint(localPoint) ? localPoint.x - frameBound.x : uint.MAX_VALUE);
					var final_y:uint = (frameBound.containsPoint(localPoint) ? localPoint.y - frameBound.y : uint.MAX_VALUE);

					if (final_x != uint.MAX_VALUE && final_y != uint.MAX_VALUE)
					{
						var pixel:uint = m_BitmapData.getPixel32(clipping.x + final_x, clipping.y + final_y);
						if (uint((pixel >> 24) & 0xFF) == 0)
						{
							return null;
						}
					}
					else
					{
						return null;
					}
				}
			}
			return super.hitTest(localPoint, forTouch);
		}
	}
}

test.png
test
test.xml

<?xml version="1.0" encoding="UTF-8"?>
<textureAtlas imagePath="test.png">
    <subTexture name="run0001" x="2" y="2" width="94" height="156" frameX="0" frameY="-4" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0002" x="98" y="2" width="94" height="150" frameX="0" frameY="-10" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0003" x="194" y="2" width="98" height="148" frameX="-1" frameY="-12" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0004" x="294" y="2" width="100" height="154" frameX="0" frameY="-6" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0005" x="396" y="2" width="98" height="160" frameX="0" frameY="0" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0006" x="2" y="164" width="96" height="160" frameX="0" frameY="0" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0007" x="100" y="164" width="96" height="158" frameX="-1" frameY="-2" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0008" x="198" y="164" width="96" height="152" frameX="-1" frameY="-8" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0009" x="296" y="164" width="98" height="150" frameX="-1" frameY="-10" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0010" x="396" y="164" width="100" height="156" frameX="0" frameY="-4" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0011" x="2" y="326" width="98" height="160" frameX="0" frameY="0" frameWidth="100" frameHeight="160"/>
    <subTexture name="run0012" x="102" y="326" width="96" height="160" frameX="-1" frameY="0" frameWidth="100" frameHeight="160"/>
</textureAtlas>
Posted on 2 Comments

Starling的Display Objects介紹(四)

上一篇:Starling的Display Objects介紹(三)
下一篇:Starling的Display Objects介紹(五)

這篇會介紹下圖裡的starling.display.Quad以及starling.display.Image
class_hierarchy

Quad物件介紹

官方手冊在此:http://doc.starling-framework.org/core/starling/display/Quad.html

A Quad represents a rectangle with a uniform color or a color gradient.

每一個端點都可以設定一個顏色,會呈現線性漸層。要顯示一個線性的漸變色,需要把一個色值設置給頂點0和頂點1,然後另一個顏色給頂點2和頂點3。

頂點位置是這樣安排的::

      0 - 1
      | / |
      2 - 3

在這個物件裡面,可以看到一個叫做mVertexData的屬性。在Starling Framework簡介的地方曾經說過,Starling是基於Stage3D而建的,關於3D渲染基礎原理請見此:3D渲染基礎原理VertexData官方介紹。所謂3D其實是一組3維的坐標,經由矩陣轉換,將之投影到2D螢幕上,計算其在2D畫面上的2D坐標,如下圖所示:
201212141827425715201212141827546379
在Starling裡面,因為是2D遊戲,則是使用正交投影的方式,直接去掉z軸的值,來將頂點坐標從3維轉至2維。但操作原理不變,這邊的則是要輸入四邊型的頂點數據。正交投影示意如下圖:
201212141827541985

下面是產生寬高各為200,200的方塊的簡單範例:

package {
	import starling.display.Quad;
	import starling.display.Sprite;
	import starling.events.Event;
	public class Game extends Sprite {
		private var q:Quad;
		public function Game() {
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded ( e:Event ):void {
			q = new Quad(200, 200);
			q.color = 0x00FF00;
			q.x = stage.stageWidth - q.width >> 1;
			q.y = stage.stageHeight - q.height >> 1;
			addChild ( q );
		}
	}
}

Image物件介紹

官方手冊在此:http://doc.starling-framework.org/core/starling/display/Image.html

一個圖片(Image)其實就是一個四邊形上面映射了一個紋理(Texture),以下圖來說,兩個三角形是Quad的範圍,而Texture就是藍色衣服的娃娃的PNG圖檔,代表貼圖的材質。

螢幕快照 2014-02-11 下午4.32.05
Image相當於Flash的Bitmap類的Starling版本。但是Starling是用Texture來代替BitmapData來為圖像提供像素數據。 要顯示一個Texture,需要把它映射到一個四邊形上,而這就是Image這個類所實現的功能。

一個”Image”是繼承自”Quad”的,我們可以在Quad上設置顏色,對於每個像素來說,最終的顏色是根據紋理的顏色和四邊形的顏色相乘而來的。 這樣您就可以很容易用Quad的顏色改變一個紋理的色調。您還可以在不改變任何四邊形的頂點坐標的情況下,在一個圖片的內部移動紋理。用此功能,可以以一個非常有效的方式,創建一個矩形遮罩。

下面是一個簡單的Image使用範例:
package {
import flash.display.Bitmap;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
import starling.textures.Texture;
import starling.utils.deg2rad;
public class Game2 extends Sprite {
private var sausagesVector:Vector. = new Vector.(NUM_SAUSAGES, true);
private const NUM_SAUSAGES:uint = 400;

[Embed(source = “boy.png”)]
private static const Sausage:Class;

public function Game2() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}

private function onAdded (e:Event):void {
// create a Bitmap object out of the embedded image
var sausageBitmap:Bitmap = new Sausage();
// create a Texture object to feed the Image object
var texture:Texture = Texture.fromBitmap(sausageBitmap);
for (var i:int = 0; i < NUM_SAUSAGES; i++) { // create a Image object with our one texture var image:Image = new Image(texture); // set a random alpha, position, rotation image.alpha = Math.random(); // define a random initial position image.x = Math.random()*stage.stageWidth image.y = Math.random()*stage.stageHeight image.rotation = deg2rad(Math.random()*360); // show it addChild(image); // store references for later sausagesVector[i] = image; } } } }[/code]

Posted on 1 Comment

Starling的Display Objects介紹(三)

上一篇:Starling的Display Objects介紹(二)
下一篇:Starling的Display Objects介紹(四)

這篇會介紹下圖裡的starling.display.Button以及starling.display.Sprite
class_hierarchy

Button物件介紹

官方手冊在此:http://doc.starling-framework.org/core/starling/display/Button.html

和Flash的SimpleButton的不同點,是它只有兩個狀態的圖檔,也就是upState與downState,而沒有over的狀態。它的disabled樣式可以經由alphaWhenDisabled的屬性去設定Disabled時的圖檔透明度。

另外,按下Button會觸發的事件是Event.TRIGGERED,因此若我們要監聽一個Button按下的事件,可用下面的方式去監聽:

menuContainer.addEventListener(Event.TRIGGERED, onTriggered);
private function onTriggered(e:Event):void {
    // outputs : [object Sprite] [object Button]
    trace ( e.currentTarget, e.target );
    // outputs : triggered!
    trace ("triggered!");
}

這是starling.display.Button一個簡單的使用範例:

package {
	import flash.display.Bitmap;
	import starling.display.Button;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;

	public class Game extends Sprite {
		[Embed(source = "../media/textures/button_up.png")]
		private static const Button_UP:Class;

		[Embed(source = "../media/textures/button_down.png")]
		private static const Button_DOWN:Class;

		public function Game() {
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}

		private function onAdded (e:Event):void {
			// create a Bitmap object out of the embedded image
			var buttonSkinUp:Bitmap = new Button_UP();
			var buttonSkinDown:Bitmap = new Button_DOWN();

			// 創建按鈕
			var myButton:Button = new Button(Texture.fromBitmap(buttonSkinUp), "這是按鈕", Texture.fromBitmap(buttonSkinDown));

			// 創建Menu容器並把按鈕加上去
			var menuContainer:Sprite = new Sprite();
			menuContainer.addChild(myButton);

			// centers the menu
			menuContainer.x = stage.stageWidth - menuContainer.width &gt;&gt; 1;
			menuContainer.y = stage.stageHeight - menuContainer.height &gt;&gt; 1;

			// show the button
			addChild(menuContainer);
		}
}
}

要注意的是,在starling裡面沒有hit area的設計,因為圖片有Bounds和Frame兩個屬性,所以要確定是否是碰到bounds的話,可以覆寫原有的hitTest函數,範例如下:

public override function hitTest( localPoint:Point, forTouch:Boolean=false ) :DisplayObject
{
	// on a touch test, invisible or untouchable objects cause the test to fail
	if( forTouch &amp;&amp; (!visible || !touchable) )
	{
		return null;
	}

	// otherwise, check bounding box of hitArea (which is a Rectangle here)
	var result:DisplayObject = null;
	if( _hitArea.containsPoint( localPoint ) )
	{
		result = this;
	}

	return result;
}

若是要偵測是否碰到透明區塊,則要使用原生的BitmapData.hitTest去測試,使用方法如下:

public hitTest(firstPoint:Point, firstAlphaThreshold:Number, secondObject:Object, [secondBitmapPoint:Point], [secondAlphaThreshold:Number]) : Boolean

Sprite物件

Sprite官方手冊在此:http://doc.starling-framework.org/core/starling/display/Sprite.html

DisplayObjectContainer官方手冊在此:http://doc.starling-framework.org/core/starling/display/DisplayObjectContainer.html

除了DisplayObjectContainer的功能以外,還多了一個flatten的屬性,這個屬性可以利用一些讓這個Sprite在播放動畫時更加的順暢,但若是開啟了flatten機制,所有之後對畫面的alpha、rotation和位置的改變都無法在畫面上被呈現。所以在使用此一屬性時要更加的小心。

Sprite一個專門用來放許多其他元件的標準容器,它可被偵聽下面這些事件:

  • 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.
  • KeyboardEvent.KEY_DOWN: 當按下按鍵時觸發
  • KeyboardEvent.KEY_UP: 當離開按鍵時觸發
  • EnterFrameEvent.ENTER_FRAME: 影格前進時觸發事件,根據frameRate的設定來決定一秒觸發幾次
    [SWF(frameRate="60", width="1024", height="768", backgroundColor="0x000000")]
  • Event.FLATTEN: 當開啟或關閉flatten功能時會觸發事件/li>
Posted on 2 Comments

Starling的Display Objects介紹(二)

上一篇:Starling的Display Objects介紹(一)
下一篇:Starling的Display Objects介紹(三)

這篇會介紹下圖裡的starling.text.TextField
class_hierarchy

TextField物件介紹

官方手冊請見此:http://doc.starling-framework.org/core/starling/text/TextField.html

下面是一個最簡單的使用範例:

package {

	import starling.display.Sprite;
	import starling.events.Event;
	import starling.text.TextField;

	public class Game extends Sprite {

		public function Game() {
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}

		private function onAdded (e:Event):void {
			// create the TextField object
			var legend:TextField = new TextField(300, 300, "簡單的文字範例", "Verdana", 38, 0xFFFFFF);
			// centers the text on stage
			legend.x = stage.stageWidth - legend.width &gt;&gt; 1;
			legend.y = stage.stageHeight - legend.height &gt;&gt; 1;
			// show it
			addChild(legend);
		}
	}
}

內嵌字型方式比較

在Starling裡的TextField可以用兩種方式去嵌入特殊字型,如下圖所示:
2014-01-17_153055
從上圖我們可以發現,原始的字體嵌入方式,在1到2的部份,將字型從ttf裡抽取出來壓成Bitmap Snapshot,這部份會需要使用到CPU來做運算。因為會多一個程序,在效能上,會比右邊的方式慢一些。Bitmap Font則是使用一份字型列表的圖檔(如下圖),去比對xml的字型資訊檔,它完全只會用到GPU的運算,在效能上優於傳統字體嵌入的方式。
fontRegular字型範例下載:fontRegular

但是若我們要求字體在各種解析度都要能夠有很高的解析,由於原始的字型嵌入方式是即時將字型轉換成Bitmap圖檔,在各種大小都可以有良好的表現。而若我們想要使用不同大小的Bitmap字型,則需要在產生文字圖檔時就決定要使用那些大小的字型,而不能在執行時才決定要顯示的文字大小為何。

在下面的Bitmap Font產生工具裡都可以有設定的選項,包括要使用那些文字、字體、等等…,以及字型要叫什麼都可以在產生時設定好。

Bitmap Font產生工具

這邊有幾個可以產生Bitmap文字圖檔及.fnt檔案的工具:

  1. Glyph Designer(適用於Mac)
    螢幕快照 2014-02-07 下午9.58.42
  2. BMFont(適用於Windows)
    螢幕快照 2014-02-07 下午9.59.34

內嵌字型實作範例

下面我們簡介一下這兩種方式的實作方式:

  1. Standard TrueType fonts:也就是一般內嵌字型的方式,將.ttf的文字檔案直接用embed的方式嵌入swf裡。
    下面是一個簡單的範例:

    package {
    	import flash.text.Font;
    	import starling.display.Sprite;
    	import starling.events.Event;
    	import starling.text.TextField;
    
    	public class Game extends Sprite {
    		[Embed(source='/../media/fonts/Abduction.ttf', embedAsCFF='false', fontName='Abduction')]
    		public static var Abduction:Class;
    
    		public function Game() {
    			addEventListener(Event.ADDED_TO_STAGE, onAdded);
    		}
    
    		private function onAdded (e:Event):void {
    			// create the font
    			var font:Font = new Abduction();
    			// create the TextField object
    			var legend:TextField = new TextField(300, 300, "使用內嵌字型的簡單範例!", font.fontName, 38, 0xFFFFFF);
    			// centers the text on stage
    			legend.x = stage.stageWidth - legend.width &gt;&gt; 1; legend.y = stage.stageHeight - legend.height &gt;&gt; 1;
    			// show it
    			addChild(legend);
    		}
    	}
    }
    
  2. Bitmap fonts:If you need speed or fancy font effects, use a bitmap font instead. That is a font that has its glyphs rendered to a texture atlas. To use it, first register the font with the method registerBitmapFont, and then pass the font name to the corresponding property of the text field.
    一個使用Bitmap來當作字型檔案的簡單範例如下:
    字型檔案下載:fontRegular

    package
    {
    	import flash.display.Bitmap;
    	import starling.display.Sprite;
    	import starling.events.Event;
    	import starling.text.BitmapFont;
    	import starling.text.TextField;
    	import starling.textures.Texture;
    	import starling.utils.Color;
    
    	public class Game extends Sprite {
    
    		[Embed(source = "../media/fonts/fontRegular.png")]
    		private static const BitmapChars:Class;
    		[Embed(source="../media/fonts/fontRegular.fnt", mimeType="application/octet-stream")]
    		private static const BritannicXML:Class;
    
    		public function Game() {
    			addEventListener(Event.ADDED_TO_STAGE, onAdded);
    		}
    
    		private function onAdded (e:Event):void {
    			// creates the embedded bitmap (spritesheet file)
    			var bitmap:Bitmap = new BitmapChars();
    			// creates a texture out of it
    			var texture:Texture = Texture.fromBitmap(bitmap);
    
    			// create the XML file describing the glyphes position on the spritesheet
    			var xml:XML = XML(new BritannicXML());
    			// register the bitmap font to make it available to TextField
    			TextField.registerBitmapFont(new BitmapFont(texture, xml));
    			// create the TextField object
    			var bmpFontTF:TextField = new TextField(400, 400, "使用內嵌字型的簡單範例!", "BritannicBold", 10);
    			// the native bitmap font size, no scaling
    			bmpFontTF.fontSize = BitmapFont.NATIVE_SIZE;
    			// use white to use the texture as it is (no tinting)
    			bmpFontTF.color = Color.WHITE;
    			// centers the text on stage
    			bmpFontTF.x = stage.stageWidth - bmpFontTF.width &gt;&gt; 1;
    			bmpFontTF.y = stage.stageHeight - bmpFontTF.height &gt;&gt; 1;
    			// show it
    			addChild(bmpFontTF);
    		}
    	}
    }

參考資料

  1. The Starling Manual » Displaying Text
  2. TextField API
  3. Starting with Starling – Ep 10: Text and Fonts
Posted on 2 Comments

Starling的Display Objects介紹(一)

上一篇:Starling Framework簡介
下一篇:Starling的Display Objects介紹(二)

Starling framework啟動方式

所有使用Starling framework的應用程式的起始點,都是由一個Starling物件,來創建整個Starling世界(Starling物件說明可見此:Starling),下面是一個最簡單的範例:

package{
    import starling.core.Starling;
    import starling.display.Sprite;

    [SWF(width="1280", height="752", frameRate="60", backgroundColor="#000000")]
    public class Startup extends Sprite
    {
        private var mStarling:Starling;
        public function Startup() {
            // 建立Starling物件
            mStarling = new Starling(Game, stage);
            // start it!
            mStarling.start();
        }
    }
}

Starling世界的物件樹

官方的說明文件裡可以看到在Starling裡,畫面物件的繼承樹是長這樣的:
class_hierarchy
從上圖可看出在Starling裡面,DisplayObject是一切畫面物件的祖先,繼承於EventDispatcher。

DisplayObject物件介紹

官方手冊請見此:http://doc.starling-framework.org/core/starling/display/DisplayObject.html

這是所有物件樹的根物件,他的子孫包含像Quad、Image、MovieClip,這一邊是在做動畫影片的。另一邊像是DisplayObjectContainer則是放物件的容器,顧名思義,它可以再加入其他的DisplayObject在裡面。反向來說,在Starling世界裡面的MovieClip,是沒有辦法使用addChild的,它只能像是一個動畫影片一樣的單純被播放、控制位置、旋轉、或偵測碰撞,而無法在MovieClip裡面加入其他的MovieClip

在Starling的DisplayObject其實只是一個abstract class,他沒有辦法被直接new出來,一定要被繼承並實作這兩個方法:

//要實作要如何利用Stage3D來產生畫面的方式
function render(support:RenderSupport, parentAlpha:Number):void;
//實作物件碰撞偵測的方式
function getBounds(targetSpace:DisplayObject, resultRect:Rectangle=null):Rectangle

從上面的繼承樹我們可以發現MovieClip與DisplayObjectContainer是採用不一樣的碰撞偵測方法以及圖形render方式的。

在Starling裡的DisplayObject則有偵測多點觸控,以及設定物件坐標、及與不同層DisplayObjectContent的坐標轉換功能。

Stage物件介紹

官方手冊請見此:http://doc.starling-framework.org/core/starling/display/Stage.html

Starling物件有它主場景物件Stage,也就是上圖的Starling.display.Stage,它和flash裡的Stage一樣,是放所有物件的母容器(The Starling stage object, which is the root of the display tree that is rendered)。

這個物件裡面只能夠有繼承於starling.display.DisplayObject的物件,而無法包含任何flash.display.DisplayObject相關的物件,這兩種類別是完全不能兼容的。雖然在Starling裡面,可以用

Starling.current.nativeOverlay.addChild(textInput);

來加入原生flash物件,但就如我前一篇簡介所說,其實他也只是會取得flash.display.Stage的物件把元件放到裡面而已,它永遠都會被放在畫面的最上層。
螢幕快照 2014-02-01 下午11.45.51

Stage會在創建Starling物件時被自動創建,所以不用自己去建立它。

參考資料

  1. The Starling Manual > Display Objects
  2. Display Object API
  3. Juggler API
  4. Starling API
Posted on 2 Comments

Starling Framework簡介

下一篇:Starling的Display Objects介紹(一)

Starling的技術基礎

Starling是基於Stage3D來實作的,因此在學習Starling之前,我建議應該要對Stage3D的特性有基礎的了解。有關於Stage3D的介紹可以看這篇文章:Stage3D運作原理。在這篇文章裡,我們可以知道Stage3D比起OpenGL及DirectX等傳統3D的APIs的優勢與劣勢。

Stage3D會依發佈平台的不同而使用不一樣的GPU運算引擎,例如在mac電腦上所使用的是OpenGL,而在Windows則會視電腦所使用的硬體設備去選擇所使用的基礎技術。值得一提的是,即使遇到不支援的硬體,Stage3D仍然可以使用軟件模式去做3D運算,只是會變得十分的緩慢。這邊有一個簡單的性能比較:Stage3D vs WebGL 性能較量

下圖是Starling的技術基礎示意圖:

image

Starling運作模式

過去我們很容易因為Stage3D這個名字而誤會這是專門為3D而做的引擎,而不能使用在2D的技術上。3D引擎的運算與2D運算最大的差異點,在於3D畫面是由一個個三角型為基礎,去建構整個畫面。而2D的向量圖形,則是由多個頂點組合而成的複雜多邊型。

要將3D的技術使用在2D上,便是將圖片整理成png或jpg等圖片,然後將圖片視為兩個三角型去繪製物件,再加上貼材質的方式,將人物的圖片貼至範圍裡。這樣便可以利用GPU去執行它最擅長的三角型繪製以及材質貼圖的動作。

 

螢幕快照 2014-02-01 下午11.37.51

Starling簡單範例

這是一個最簡單的Starling Sample:StarlingTest
StarlingTest.as

package
{
	import flash.display.Sprite;

	import starling.core.Starling;

	[SWF(frameRate="60",Width="800",Height="600")]
	public class StarlingTest extends Sprite
	{
		public function StarlingTest()
		{
			var star:Starling = new Starling(Main, stage);
			star.start();
		}
	}
}

Main.as

package
{
	import starling.core.Starling;
	import starling.display.MovieClip;
	import starling.display.Sprite;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;

	public class Main extends Sprite
	{
		[Embed(source = 'test.xml', mimeType = 'application/octet-stream')]
		private var AtlasXML:Class;

		[Embed(source = 'test.png')]
		private var AtlasTexture:Class;

		public function Main()
		{
			var texture:Texture = Texture.fromBitmap(new AtlasTexture());
			var xml:XML = XML(new AtlasXML());
			var atlas:TextureAtlas = new TextureAtlas(texture, xml);

			var mc:MovieClip = new MovieClip(atlas.getTextures("run"),30);
			addChild(mc);

			Starling.juggler.add(mc);
		}
	}
}

全新的影格運作方式

從上面的Code來看,Flash programmer似乎會覺得好像和原本的程式幾乎一模一樣,其實這是Staring刻意希望讓程式師可以無痛學習Starling,刻意去這樣設計的。在很像的外表下,若我們使用Starling去建置我們的flash程式,不論在影格、圖形渲染方式、碰撞偵測方式、事件傳遞方式等。其實與原生程式,在根本上是完全不一樣的東西。也因為在基本概念上就有很大的不同(starling是以3D去模擬2D的環境,而原有的Flash則是完全使用CPU去做畫面的運算)。我建議所有Flash的開發者還是要以全新的角度去學習這套framework,並且要了解其根本的運作原理,這樣在操作上才不會被它與原生語言的不同之處搞混淆。

因此我們要知道,上面範例裡面的starling.display.MovieClip與原生的flash.display.MovieClip在本質上便已經是完全不同的東西了。在表現的圖層上也並不相同,下圖是圖層的示意圖:
螢幕快照 2014-02-01 下午11.45.51
從上圖可以看到,所有原來我們用flash.display.*去產生的畫面,一律會被放在最上面那層的『Display List』,而所有stage3D(在這裡也就是Starling)的物件則會放在它下面。另外StageVideo則是可以使用GPU去播放的影片,則會放在最下面一層。

這也可以看出,若是想要採用starling去開發你的應用程式,不太可能混合starling和原生的flash DisplayObject一起使用,因為原生的flash所產生的畫面會永遠的被放在starling之上(請見註一)。另外,若是使用starling,在嵌入swf至網頁時,一定要將wmode設成direct,starling不支援透明的嵌入方式,否則會跳出如下圖的錯誤訊息。

螢幕快照 2014-02-07 下午2.42.20

另外,雖然Starling很多類別的名稱或屬性都很相似,或者一樣,但用法其實也是有很大的不同。這邊是Starling的API手冊:Starling Framework Reference

註一:
我們可以利用nativeOverlay將原生的flash的displayObject放到starling的場景上。
請見下面的範例,下面範例是如何在starling裡加入可打字的文字框的方式。

var textInput:flash.text.TextField = new flash.text.TextField();
textInput.type = TextFieldType.INPUT;
Starling.current.nativeOverlay.addChild(textInput);

因為starling的textField本身是不能直接設定為可打字的,因此如果想要在場景上加一個可輸入文字的文字欄位,這邊有很詳細的方法介紹:Text Input with Starling framework
不過要注意,使用nativeOverlay所加進場景裡的物件,也是永遠會在最上層。下面是官網對於Native overlay的說明:

Sometimes you will want to display native Flash content on top of Starling. That’s what the nativeOverlay property is for. It returns a Flash Sprite lying directly on top of the Starling content. You can add conventional Flash objects to that overlay.

Beware, though, that conventional Flash content on top of 3D content can lead to performance penalties on some (mobile) platforms. For that reason, always remove all child objects from the overlay when you don’t need them any longer. Starling will remove the overlay from the display list when it’s empty.

參考資料

Posted on

套件管理工具CocoaPods介紹

這是1/9的cocoaHeads裡,SuperBil分享的套件管理工具。
之前小岡也有和我推薦過這個工具,當時沒有去深入研究如何使用。
這次與會完後,便開始試著學習使用這個管理套件。(裝完後心得:天呀!實在太好用了!必裝~)

分享資料

投影片:做自己的可可豆夾
錄影檔:CocoaPods

CocoaPods介紹

CocoaPods是一個管理套件的工具。

過去在開發app時,如果我們想要用一些第三方的Library,通常會要到GitHub下載專案到本地端,然後把它載入專案裡。這樣如果套件有更新時,都要手動更新,若是不同版本的ios要用到不同的library,又要手動去管理,會比較麻煩。

並且如果是直接把原始碼放到專案裡,會很容易和自己寫的code混在一起,管理和瀏覽都會較為困難。

CocoaPods就是用來管理這些第三方套件,使用CocoaPods之後,專案會變這樣:

螢幕快照 2014-01-12 下午4.48.50
要改成點選GourmentGroup.xcworkspace開啟專案
螢幕快照 2014-01-12 下午4.44.44
下方會多一個Pods的套件資料,裡面放的是所有使用到的套件

 安裝方法

這篇文章有非常詳細的方法:CocoaPods

比較要注意的是,我今天在安裝時,因為Podfile檔案所使用的編碼錯誤,會出現如下錯誤
incompatible character encodings: ASCII-8BIT and UTF-8 cocoaPods
後來我雖然把檔案改成utf-8,還是一直跑出同樣的錯誤。
後來才發現,如果有錯誤,要先把終端機關掉再打開,才會再一次執行。

接下來,如果要更新Podfile,到終端機打入
$pod update
就可以了!

投影片另外有講到podSpec,如果有在cocoaPods裡面沒有包含的Library
(現有的Library可到http://cocoapods.org/去輸入函式庫的名稱找有沒有現有的)
如果沒有的話(或者是自己製作的Library),就可以自己去寫spec
這邊有教學:Specs and the Specs Repo

參考資料

Posted on

在Windows下產生.p12及.mobileprovision

在 Windows 產生憑證簽名要求

對 Windows 開發人員而言,最簡單的方法是取得 Mac 電腦上的 iPhone 開發人員憑證。不過,他們也可以在 Windows 電腦上取得憑證。首先,使用 OpenSSL 建立憑證簽名要求 (CSR 檔):

  1. 在 Windows 電腦上安裝 OpenSSL (移至http://www.openssl.org/related/binaries.html,或直接在此下載openssl-0.9.8k_X64)。
    開啟 Windows 命令工作階段,然後使用 CD 命令切換至 OpenSSL bin 目錄 (例如 c:\OpenSSL\bin\)。在命令列輸入以下命令以建立專用密鑰:
    openssl genrsa -out mykey.key 2048
    儲存此專用密鑰。您稍後將會用到它。使用 OpenSSL 時,請勿忽略錯誤訊息。OpenSSL 即使產生錯誤訊息,可能仍會輸出檔案。但這些檔案可能無法使用。如果發生錯誤,請檢查您的語法並重新執行命令。
  2. 在命令列輸入以下命令以建立 CSR 檔:
    openssl req -new -key mykey.key -out CertificateSigningRequest.certSigningRequest -subj “/emailAddress=yourAddress@example.com, CN=John Doe, C=US”
    以您自己的值取代電子郵件地址、CN (憑證名稱) 及 C (國家/地區) 值。

至APPLE申請.cer檔案

  1. https://developer.apple.com/membercenter/index.action以開發者身份登入。
  2. 選擇Certificates, Identifiers & Profiles,如下圖
    2014-01-07_153638
  3. 選擇Certificates
    2014-01-07_153659
  4. 接著按右上角的+
    2014-01-07_155542
  5. 身份選【iOS App Development】
    2014-01-07_155602
  6. 在最後會要求上傳.certSigningRequest,選擇剛剛在【在 Windows 產生憑證簽名要求】所產生的檔案,按確認。
    2014-01-07_155713
  7. 下載.cer檔
    2014-01-07_155843

將開發人員憑證轉換成 P12 檔案

  1. 將從 Apple 收到的開發人員憑證檔案轉換成 PEM 憑證檔案。從 OpenSSL bin 目錄執行以下命令列陳述式:
    openssl x509 -in developer_identity.cer -inform DER -out developer_identity.pem -outform PEM
  2. 現在可以根據 iPhone 開發人員憑證的密鑰及 PEM 密鑰,產生有效的 P12 檔案:
    openssl pkcs12 -export -inkey mykey.key -in developer_identity.pem -out iphone_dev.p12

產生.mobileprovision檔案

  1. 首先先產生一個APP的ID(已註冊過1~2項可跳過),選左側的”Identifier=>App IDs”,註冊一個APP的ID。
    2014-01-07_160759
  2. 填寫你要做的APP的前置字等識別,若是只想練習則選”Wildcard App ID”,然後Bundle ID填入”*”
    2014-01-07_161119
  3. 接著註冊你的測試機(已註冊過3~5項可跳過),再選左邊側欄的Devices=>All,新增開發機的UUID
    2014-01-07_162122
  4. 打開iTunes,連接iPhone,打開摘要頁,點紅框處的序號一下,會變成顯示UUID,把它記下來。
    2014-01-07_162323
  5. 回到網頁,把上面的UUID填入下面UUID那欄,並且為這隻手機取個名字
    2014-01-07_162508
  6. 現在要去產生.mobileprovision檔案。點選左邊側欄的”Provisioning Profiles”,接著按右上的+號新增一個Profiles
    2014-01-07_160633
  7. 身份選擇【iOS App Development】
    2014-01-07_160650
  8. App ID選擇剛剛所產生的ID
    2014-01-07_161714
  9. 選擇剛剛產生的certificates身份
    2014-01-07_161843
  10. 選擇要測試發佈的手機
    2014-01-07_163645
  11. 為這個發佈設定命名
    2014-01-07_163655
  12. 下載.mobileprovision檔案,完成。

在Flex裡設定發佈檔案

  1. 在專案點右鍵,選Properties,在此選擇要使用的檔案
    2014-01-07_164424
Posted on

我的第一款APP企劃

畫面繪製工具: Balsamiq Mockups
參考規範: iPhone-HIG(人機界面指南)
企劃圖片下載: APP開發

名稱:94愛吃
類別:生產力應用程式
分類:美食
簡介:
想揪三五好友去吃某間超好吃的餐廳時,卻總是忘了餐廳的詳細資訊嗎?
想找一間適合談公事的餐廳,卻又一時想不起究竟那裡適合嗎?

這一款APP可以幫助你記錄曾吃過的餐廳,以及想吃卻還未去吃的餐廳。
也可以以地圖的方式,去告訴你這附近有那些你吃過的餐廳可以去吃。更能提供你最新、最酷的焦點餐廳情報。

如果你是美食愛好者,那不能錯過這一款APP唷!

畫面截圖

PLAN
第一頁: 可用月曆模式瀏覽紀錄
PLAN_2
第二頁: 以列表方式顯式資料
PLAN_3
按進階搜尋後畫面,按搜尋之後會反回第二頁只是資料不同
PLAN_4
點選列表或日曆的單項說明後進入詳細資訊頁
PLAN_5
點第一、第二頁右上的新增後進入的新增畫面
PLAN_6
第三頁:以地圖去尋找附近的餐廳(未來可能會想改成可以找網友分享的紀錄)
PLAN_7
第四頁: 純粹顯示一個網頁,如果未連網則顯示沒有連網頁,這邊應該會用網頁去更新,提供好吃餐廳資訊
PLAN_8
第五頁: 介紹作者

因為是第一款APP,不知道這樣會不會觸犯到APP的上架條款

  1. 應用程式涉及了裸體、暴力、中傷他人
  2. 違反商標法
  3. 功能過於簡單或與iOS內建功能動覆
  4. 使用未開放的API
  5. 應用程式Bug、當機且無任何警告訊息
  6. 違反HIG(Human Interface Guide)使用者介面
  7. 軟體功能與描述不符,或是描述提到第三方的名詞,如:Android/BlackBerry
  8. 簽證檔需設定為Distribution(iOS Dev Center中設定)

希望有經驗的人可以給我一些建議,謝謝大家!