Sunday, March 29, 2009

[Flash] Zinc 3

[Blog] Comment Section

  • Comment Section
  • 以往的經驗是幾乎沒有人會在我的blog上回文,多半都是直接mail給我,所以久而久之就忘了檢查這類的訊息。直到最近在整理文章時才赫然發現居然有人回文,之前未能及時回文,這裡跟您們說聲抱歉。

    小小紀錄一下blogspot新增comment section的方式。

    版面配置->新增小工具->資訊提供,輸入你要顯示的網頁,以我的部落格為例如下:

    http://nxforce.blogspot.com/feeds/comments/summary

    將紅色字替換成你的blog id即可。

[Flash] Performance Tips for CPU Usage

  • Performance Tips for CPU Usage
  • 提供一些基本的Actionscript 3.0 最佳化的寫法。

    做整數運算使用 int 會比 Number 快許多,int 又比 uint還要快一點。

    不良的寫法如下:

    var n:Number;
    n++;

    for(var i:Number=0; i<100;i++){}

    較好的寫法如下:

    var n:int;
    n++;

    for(var i:int=0; i<100;i++){}

    宣告變數時給予明確的型態會比宣告成Object快許多。

    不良的寫法如下:

    var i;
    var j:Object;
    var k:*;

    較好的寫法如下:

    var i:int;
    var j:Boolean;
    var k:Number;

    型態轉型使用 Type(variable) 會比使用 as 或者其他轉換函式快許多

    不良的寫法如下:

    var n:int = 100;
    var m:String = n.toString();

    var o:Object;
    o as int;

    較好的寫法如下:

    var n:int = 100;
    var m:String = String(n)();

    var o:Object;
    int(o);

    直接讀取變數值會比透過物件來取值快許多

    不良的寫法如下:

    var obj:Object = {"n":1000} ;
    for(var i:int=0; i<obj.n;i++){}

    較好的寫法如下:

    var obj:Object = {"n":1000} ;
    var n:int = obj.n;
    for(var i:int=0; i<n;i++){}

    陣列複製使用concat()會比loop複製快

    不良的寫法如下:

    var arr1:Array = new Array(100000);
    var arr2:Array = new Array(100000);
    for(var i:int = 0; i<arr1.length ; i++){
    arr1[i] = arr2[i];
    }

    較好的寫法如下:

    var arr1:Array = new Array(100000);
    var arr2:Array = new Array(100000);
    arr2 = arr1.concat();

    基本數學運算使用自行客制化的會比使用Math class快

    不良的寫法如下:

    Math.floor(0.9);
    Math.abs(-1);
    Math.abs(n,2);

    較好的寫法如下:

    int(0.9);
    n>0?n:-n;
    n*n;

    想要了解更多關於Actionscript的最佳化可以參考下面的網頁

    http://www.rozengain.com/blog/2007/05/01/some-actionscript-30-optimizations/

Tuesday, March 24, 2009

[Flash] Copy to Clipboard

  • Copy to Clipboard
  • 在一些比較人性化的設計裡,你可能會需要將Flash上的文字輸出複製到剪貼簿裡,這裡提供適用於Flash CS3/4的方法。

    假設你有一個TextField的物件名稱為txtField。

    For Flash CS3 (Flash Player 9)

    import flash.system.System;

    System.setClipboard(txtField.text);

    For Flash CS4 (Flash Player 10)

    import flash.desktop.Clipboard;
    import flash.desktop.ClipboardFormats;
    import flash.desktop.ClipboardTransferMode;

    Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT, txtField.text);

    實際使用的結果是CS4的方法比較能將文字格式完整複製到剪貼簿上。

    http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/System.html

    http://help.adobe.com/en_US/AS3LCR/Flash_10.0/flash/desktop/Clipboard.html

Monday, March 23, 2009

[Flash] Export to Image

  • Export to Image
  • 使用PNGEncoder/JPEGEncoder配上bitmapdata可以將Sprite上的顯示物件轉成Image。

    For Flash:

    編碼可以從這一篇取得[Flash] AS3 Core Library

    package{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.utils.ByteArray;
    import flash.display.BitmapData;
    import flash.net.FileReference;

    import com.xinyu.button.TextButton;
    import com.adobe.images.PNGEncoder;

    public class EncodeDemo extends Sprite{
    private var mainUI:Sprite;
    private var printBtn:TextButton;

    public function EncodeDemo(){
    mainUI = new Sprite();
    addChild(mainUI);
    mainUI.graphics.lineStyle(5,0x000000,1);
    mainUI.graphics.drawCircle(stage.stageWidth/2-25,stage.stageHeight/2-25,50);
    mainUI.graphics.endFill();

    printBtn = new TextButton(100,20,10,2,0xCCCCCC,"Export to PNG",0x000000);
    addChild(printBtn);
    printBtn.x = stage.stageWidth/2 - printBtn.width/2;
    printBtn.y = stage.stageHeight - 2*printBtn.height;
    printBtn.addEventListener(MouseEvent.CLICK, onClicked);
    }

    private function onClicked(event:MouseEvent):void{
    var bmpData:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
    bmpData.draw(mainUI);

    var pngEncoder:PNGEncoder = new PNGEncoder();
    var byteArr:ByteArray = PNGEncoder.encode(bmpData);
    var file:FileReference = new FileReference();
    file.save(byteArr, "image.png");
    }
    }
    }

    For Flex:

    <?xml version="1.0" encoding="utf-8"?>
    <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="300" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#495D7B, #303F4A]" creationComplete="initApp()">
    <mx:Script>
    <![CDATA[
    import mx.graphics.codec.PNGEncoder;
    import mx.graphics.codec.JPEGEncoder;
    import flash.filesystem.File;

    private function initApp():void{
    mainUI.graphics.lineStyle(5,0x000000,1);
    mainUI.graphics.drawCircle(this.width/2-25,this.height/2-25,50);
    mainUI.graphics.endFill();
    }

    private function onClicked():void{
    var bmpData:BitmapData = new BitmapData(mainUI.width, mainUI.height);
    bmpData.draw(mainUI);

    var pngEncoder:PNGEncoder = new PNGEncoder();
    var byteArr:ByteArray = pngEncoder.encode(bmpData);
    var file:File = File.desktopDirectory.resolvePath("");
    file.save(byteArr,"image.png");
    file.clone();
    }
    ]]>
    </mx:Script>
    <mx:VBox left="10" top="10" right="10" bottom="10" verticalGap="2">
    <mx:Panel width="100%" height="220" layout="absolute" title="PNG Encoder">
    <mx:UIComponent id="mainUI" width="100%" height="100%" />
    </mx:Panel>
    <mx:ApplicationControlBar width="100%" height="100%" cornerRadius="0">
    <mx:HBox width="100%" horizontalAlign="center" verticalAlign="middle" horizontalGap="0">
    <mx:Button label="Export to PNG" click="onClicked()"/>
    </mx:HBox>
    </mx:ApplicationControlBar>
    </mx:VBox>
    </mx:WindowedApplication>


[Flash] Printing - Orientation and Resize

  • Printing - Orientation and Resize
  • 從AS3的PrintJob說明文件看來,功能似乎很簡潔,這裡提供自己處理orientation和border的方法,範例程式碼如下:

    package {
    import flash.display.Sprite;
    import flash.printing.PrintJob;
    import flash.printing.PrintJobOrientation;
    import flash.events.MouseEvent;

    import com.xinyu.button.TextButton;

    public class PrintDemo extends Sprite {
    var sp1:Sprite ;

    public function PrintDemo() {
    sp1 = new Sprite();
    addChild(sp1);

    var printBtn:TextButton = new TextButton(80,20,10,2,0xCCCCCC,"Print",0x000000);
    sp1.addChild(printBtn);
    printBtn.x = 10;
    printBtn.y = 10;
    printBtn.addEventListener(MouseEvent.CLICK, onPrintClicked);
    }

    private function onPrintClicked(event:MouseEvent):void {
    var myPrintJob:PrintJob = new PrintJob();

    var sp2:Sprite = new Sprite();
    sp2.graphics.beginFill(0x000000);
    sp2.graphics.drawRect(0,0,400,300);
    sp2.graphics.endFill();

    this.addChild(sp2);
    sp2.addChild(sp1);

    sp1.x += 50;
    sp1.y += 50;

    if (myPrintJob.start()) {
    if(myPrintJob.orientation == PrintJobOrientation.PORTRAIT) {
    sp2.rotation = 90;
    }

    try {
    myPrintJob.addPage(sp2);
    } catch (error:Error) {

    }
    myPrintJob.send();
    }

    this.removeChild(sp2);
    this.addChild(sp1);

    sp1.x -= 50;
    sp1.y -= 50;
    }
    }
    }

    其中的TextButton是我自己的物件,你可以換成其他任何形式的按鈕物件。主要的技巧是我使用了兩個Sprite,分別為sp1:作為主要的顯示物件,sp2作為列印時的顯示物件。你可以看到當按鈕按下進入onPrintClicked()函式,我建立了sp2這個Sprite,並且繪製了400x300pixel大小的黑色底色(這裡使用黑色只是為了除錯時方便檢驗,實際列印過程中請使用白色),這樣做是避免Flash本身會最佳剪裁大小而讓我的sp2的寬長不能固定。

    接著將sp1加入到sp2裡,並且設定sp1的x,y值,來達到有邊界的效果(當然你在設計過程中要處理好sp1的寬長,免得sp1在移動之後超出sp2的底色),開使printJob時由印表機的列印方向來決定是否旋轉sp2。

    傳送完printJob之後,再將sp2從主場景移除,並將sp1加入回來。大致上多了這兩項的處理可以讓你的列印完善許多。

    PS. 如果你要列印的物件有包含到文字,經旋轉後會無法正常列出,請使用上一篇的技巧,將字體轉成embedded,[Flash] Embedded Font

[Flash] Embedded Font

  • Embedded Font
  • 在使用AS3所產生的文字物件常常會面臨到無法做放大縮小或者漸變等特效,這時你可以試著先將要使用的字體載入到Library裡,並設定embedded 模式即可。

    Step 1. 在Library Panel上的空白處按右鍵選擇"New Font",選擇你要的字體並給予一個名稱。

    Step 2. 接著滑鼠右鍵點選你剛匯入的字體,選擇linkage,給予這個字體一個class name。

    Step 3. 撰寫程式碼,如下:

    import fl.transitions.Tween;
    import fl.transitions.easing.Strong;

    var container:Sprite = new Sprite();
    addChild(container);

    var txtFmt:TextFormat = new TextFormat();
    txtFmt.font = "Verdana";
    txtFmt.size = 16;
    txtFmt.bold = true;

    var txtField:TextField = new TextField();
    container.addChild(txtField);
    txtField.autoSize = TextFieldAutoSize.LEFT;
    txtField.embedFonts = true;
    txtField.defaultTextFormat = txtFmt;
    txtField.text = "Embedded Font Effect!";

    var tween:Tween = new Tween(txtField, "alpha", Strong.easeOut, 1, 0, 5, true);

    上面的方法不適用於TextInput 模式的物件。


[Flash] setTextFormat() v.s. defaultTextFormat

  • setTextFormat() v.s. defaultTextFormat
  • 之前在使用TextField時常常被這兩個東西搞混,這裡做個小小紀錄。

    setTextFormat() :用在文字已經寫在text這個變數上時,範例如下:

    var txtFmt:TextFormat = new TextFormat();
    txtFmt.size = 16;
    txtFmt.bold = true;
    txtFmt.color = 0x0000ff;

    var txtField:TextField = new TextField();
    addChild(txtField);
    txtField.x = 100;
    txtField.y = 10;
    txtField.text = "setTextFormat method.";
    txtField.autoSize = TextFieldAutoSize.CENTER;
    txtField.setTextFormat(txtFmt);

    承接著上面的使用方式,如果要做後續的性質修改,必須使用下面的方法:

    txtFmt = txtField.getTextFormat();
    txtFmt.size = 28;
    txtField.setTextFormat(txtFmt);

    defaultTextFormat:用在文字是由執行時期所輸入的,範例如下:

    var txtFmt:TextFormat = new TextFormat();
    txtFmt.size = 16;
    txtFmt.bold = true;
    txtFmt.color = 0x0000ff;

    var txtField:TextField = new TextField();
    addChild(txtField);
    txtField.type = TextFieldType.INPUT;
    txtField.x = 100;
    txtField.y = 10;
    txtField.autoSize = TextFieldAutoSize.CENTER;
    txtField.defaultTextFormat = txtFmt;

    承接著上面的使用方式,如果要做後續的性質修改,必須使用下面的方法:

    txtFmt = txtField.defaultTextFormat;
    txtFmt.size = 28;
    txtField.defaultTextFormat = txtFmt;

    這時問題又來了,如果我又想要先預設一段文字,且又希望此TextField可以作為輸入,那你的程式要這樣寫:

    var txtFmt:TextFormat = new TextFormat();
    txtFmt.size = 16;
    txtFmt.bold = true;
    txtFmt.color = 0x0000ff;

    var txtField:TextField = new TextField();
    addChild(txtField);
    txtField.type = TextFieldType.INPUT;
    txtField.x = 100;
    txtField.y = 10;
    txtField.text = "editable TextField with setTextFormat.";
    txtField.autoSize = TextFieldAutoSize.CENTER;
    txtField.setTextFormat(txtFmt);

    ...@@

[Flash] AS3 Data Structures For Game Developers

  • AS3 Data Structures For Game Developers
  • 如同之前所提,AS3 並沒有像Java一樣提供許多方便的API使用,所以往往都是由程式設計師自行完成。這裡提供由Michael Baczynski所實做的Library,裡面包含了Linkedlist, Tree, Hash and Sorting等常見的資料結構程式碼,下載位置如下:

    http://code.google.com/p/as3ds/

    此Library在Flash CS3底下運作正常,但如果你要將他移植到Flash CS4底下撰寫時,有部份的原始碼並沒有實做interface裡的function,所以必須自行稍微修正一下。(Flash CS4的AS3語法檢查比較嚴格)

[Flash] AS3 Core Library

  • AS3 Core Library
  • 由於Flash本身所提供的library沒有像Flex多,所以往往我們必須自行實做或者下載已經包好的,下面就是由mikechambers等人主導的as3corelib,下載位置如下:

    http://code.google.com/p/as3corelib/

    裡面有包含AS3的image encoder,如此要做runtime image exporting就方便多了

Wednesday, March 18, 2009

[Flash] Type Checking

  • Type Checking
  • 在ActionScript 3.0 裡如果要做型態檢驗,可以透過 isas 這兩個operator來達成,下面是一個簡單的範例:

    import com.xinyu.geom.Ball;
    import com.xinyu.geom.Triangle;

    var ball:Ball = new Ball();
    addChild(ball);

    trace(ball is Ball);
    trace(ball is Triangle);
    trace(ball is Sprite);
    trace(ball as Ball);
    trace(ball as Triangle);
    trace(ball as Sprite);

    輸出畫面如下:

    true
    false
    true
    [object Ball]
    null
    [object Ball]

Wednesday, March 11, 2009

[Flash] Maze

Download: MazeDemo.zip

  • 按下Run可執行迷宮出口搜尋
  • 按下Stop可停止迷宮出口搜尋
  • 按下Reset可將狀態歸零
  • 按下Enable可以自訂迷宮,透過滑鼠的點擊,以及托移紅色(終點)/綠色(起點)方塊來修改。
  • 按下Disable可以關閉客制化,一旦開啟修改模式,修改完請手動關閉,否則無法執行迷宮搜尋。
  • 程式防呆處理有限,請勿做白目舉動。

迷宮算是在程式設計裡一個很經典的設計問題,絕大數的人在用程式來解迷宮時多半都使用Recursive的形式來撰寫,但是在Flash底下我則是選擇以Stack/Array的形式來設計,一方面是受限於Flash Stack Size,而另一方面則是為了方便圖形化。

下面的迷宮演算是使用一種類似stack的形式來撰寫,這種演算是建立在已知全體地圖下才適用,如果要達到在未知地圖的自由空間裡搜尋,那個演算就會稍微複雜點。

/* SimpleMazeDemo */
package {
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent;

import com.xinyu.geom.Maze;
import com.xinyu.geom.Ball;
import com.xinyu.geom.Position2D;

public class MazeDemo extends Sprite {
private var pos:Position2D;
private var maze:Maze;
private var obj:Ball;
private var path:Array;
private var iPath:uint = 0;
private var timer:Timer;

private const STEP:Number = 40;
private const DIRECTION:Array = [new Position2D(0,-1), new Position2D(0,1), new Position2D(-1,0), new Position2D(1,0)];

public function MazeDemo() {
maze = new Maze(10, 10, STEP);
addChild(maze);

path = new Array(50);
for(var i:uint = 0; i<path.length;i++){
path[i] = new Position2D();
}

obj = new Ball(0xCCCCCC, 0x000000, STEP/2, 90);
addChild(obj);
obj.x = maze.start.x * STEP + STEP/2;
obj.y = maze.start.y * STEP + STEP/2;
pos = new Position2D(maze.start.x, maze.start.y);
maze.setPath(pos);
path[iPath].x = pos.x;
path[iPath].y = pos.y;

timer = new Timer(50);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
}

private function onTimer(event:TimerEvent):void {
if (pos.x == maze.end.x && pos.y == maze.end.y) {
timer.stop();
}

if(path[iPath].dir){
if (!maze.isValid(pos, DIRECTION[path[iPath].dir-1])){
moveForward(DIRECTION[path[iPath].dir-1]);
}else{
path[iPath].dir--;
}
}else{
backTrack();
}
}

private function moveForward(dir:Position2D):void {
path[iPath].dir--;
updatePos(dir);
maze.setPath(pos);
iPath++;
path[iPath].x = pos.x;
path[iPath].y = pos.y;
}

private function backTrack():void{
var dir:Position2D = new Position2D(path[iPath-1].x - pos.x, path[iPath-1].y - pos.y);
path[iPath] = new Position2D();
maze.cleanPath(pos);
--iPath;
updatePos(dir);
}

private function updatePos(dir:Position2D):void{
pos.x += dir.x;
pos.y += dir.y;
obj.x += dir.x * STEP;
obj.y += dir.y * STEP;
}
}
}

整個核心函式其實只有moveForward()跟backTrack()這兩個而已,可以說是非常簡單。而最上面的展示範例則是依造SimpleMazeDemo的基礎擴展而成。由於要實做UI的關係,所以完整程式碼篇幅就不小,所使用的class也不少,但是發佈後的swf很小(因為所有畫面上的物件都是由AS 3.0所撰寫出來的),詳細資訊請參考下載檔。

[Flash] Stack & Queue

  • Stack & Queue
  • 雖然Flash本身並沒有提供Stack這類的Class,但是事實上你可以直接使用Array Class的pop(), push(), shift()來達到Stack or Queue的效果。這裡我則是不使用Array Class來實做。上面的範例只是小小的展示Stack(FILO)跟Queue(FIFO)的性質。

    下面程式碼是依造Stack/Queue Implemented Linked Structures這一篇所修改而來的。

    /* Node.as */
    package com.xinyu.collection{
    public class Node {
    public var next:Node = null;
    public var data:Object = null;

    public function Node(data:Object = null, next:Node = null){
    this.data = data;
    this.next = next;
    }
    }
    }

    /* Stack.as */
    package com.xinyu.collection{
    import com.xinyu.collection.Node;
    public class Stack {
    private var _top:Node;
    private var _length:Number;

    public function Stack(){
    _top = null;
    _length = 0;
    }

    public function isEmpty():Boolean {
    return _top == null;
    }

    public function push(data : Object):void {
    var newTop:Node = new Node(data, _top);
    _top = newTop;
    _length += 1;
    }

    public function pop():Object {
    if (isEmpty ()) {
    return "empty";
    }

    _length -= 1;
    var data:Object = _top.data;
    _top = _top.next;
    return data;
    }

    public function peek():Object {
    if (isEmpty ()) {
    return "empty";
    }
    return _top.data;
    }

    public function get length():Number{
    return _length;
    }
    }
    }

    /* Queue */
    package com.xinyu.collection{
    import com.xinyu.collection.Node;

    public class Queue {
    private var _bottom:Node;
    private var _top:Node;
    private var _length:Number;

    public function Queue(){
    _bottom = _top = null;
    _length = 0;
    }

    public function isEmpty():Boolean {
    return (_bottom == null);
    }

    public function enqueue(data:Object):void {
    var node:Node = new Node(data, null);
    if (isEmpty()) {
    _bottom = node;
    _top = node;
    } else {
    _top.next = node;
    _top = node;
    }
    _length += 1;
    }

    public function dequeue():Object{
    if (isEmpty()) {
    return "empty";
    }

    _length -= 1;
    var data:Object = _bottom.data;
    _bottom = _bottom.next;
    return data;
    }

    public function peek():Object {
    if (isEmpty()) {
    return "empty";
    }

    return _bottom.data;
    }

    public function get length():Number{
    return _length;
    }
    }
    }

    正常來說Stack跟Queue是不需要在意length的問題(前提是不考慮記憶體大小以及程式本身的Stack Size 等限制),而這裡我卻加了一個length變數,目的只是想要知道目前存了多少個元素(有點像ArrayCollection),當然上面的寫法是一種很簡易的實做,並沒有去限定Stack/Queue本身儲存的Data type一致的問題(也就是說Number, String, Boolean...等各種型態皆可放置在上面的 Stack/Queue裡),所以在設計時要格外注意一下型態的問題。(由於StackQueueDemo.mxml只是單純展示Stack/Queue用的,並沒有實質的意義,所以就不貼出了)