顯示具有 AS3 標籤的文章。 顯示所有文章
顯示具有 AS3 標籤的文章。 顯示所有文章

2012年2月23日 星期四

既然Flash版GoogleMap不再發新的key…



總覺得自己老是在走偏門…=_=。

自從GoogleMap Flash版停止再發送key值之後,GoogleMap的Flash版本等於正式走入歷史,也因此我們要在flash裡使用GoogleMap的話會變得很頭大。
雖然說以後全flash的網站「理論上」會越來越少,應該都會是flash搭配html一起邁向美好的未來,但是有時難免客戶(或設計師)為了整體視覺著想(或其它因素),一定要在flash使用GoogleMap的話,那就是我們要苦往肚子裡吞的時候了。
這兩天研究了一下,想說看有沒有辦法弄一個簡單的GoogleMap到flash裡,結果似乎是可行的。
說穿了,原理其實就是計算經緯度,然後跟Google「偷偷」的拿取該經緯度下的圖片,再呈現在flash裡而已。

GoogleMap的圖片區塊
可以先參考一下這一篇:
http://code.google.com/intl/zh-TW/apis/maps/documentation/javascript/v2/overlays.html#Google_Maps_Coordinates
要取得圖片的網址很簡單,瞄一下httpWatch的話就會知道,格式類似這樣:
http://mt0.googleapis.com/vt?lyrs=m@170000000&src=apiv3&hl=zh-TW&x=1&y=1&z=15&s=G&style=api|smartmaps
Google把地圖切分成很多區塊,每一個區塊(Tile)的圖片大小是256X256,在不同級數之下會有不同數量區塊,數量的增加是以2的次方在增加的。

也就是說,在縮放等級(zoom)是0的情況之下,世界地圖只有2的0次方=1張的圖片,zoom=2的時候,x軸及y軸各有2的2次方=4張圖,一共16張圖。
有了這個概念的時候,我們就可以依照zoom的大小,配上實際經緯度投射到平面座標上的點,來找出目前該經緯度所對應到的圖片,然後再從Google那邊把圖抓回來就對了,剛剛上面的圖片網址裡,x及y就是代表該區塊的index值,z則是縮放值。

經緯度的投射座標
既然已經知道地圖被切成多少區塊,就很方便取得了,但麻煩的在於經緯度。
GoogleMap使用的是「麥卡托投影法」來計算經緯度,請參考wiki:
http://zh.wikipedia.org/wiki/%E9%BA%A5%E5%8D%A1%E6%89%98%E6%8A%95%E5%BD%B1
這種投影法將地球投影成圓柱狀體,也因此,經度的部份是從最左邊的-180到最右邊的180度,平均均分地圖上的所有x軸距離。
於是我們可以很簡單的計算出某一個經度應該對應到實際像素裡的哪一個x值,因為經度是平均切割整個世界地圖的,所以純粹只是比例的換算而已。
麻煩的在於緯度。
緯度並不是平均切割地球的從南到北,越靠近極點,形變就越大。
感謝wiki,我們不用自己去導出投影的公式,wiki已經幫我們列出來了:

上半部是由經緯度計算出x及y的值,下半部是由x跟y的值反算出經緯度,由於我們要將經緯度用在GoogleMap圖片的像素值上,因此我們只要再加上一些參數做修正其實就可以了。

結果
我花了一點時間實作了一下,結果在這邊:

http://labs.medialand.com.tw/jason/flash/googlemap/
原始檔下載
輸入經緯度及放大級數,這flash就會把周邊的地圖載入進來。
其它的,例如:拖動地圖…等等的,我就沒時間再寫了,反正都已經能正確取得圖片及計算出座標值,其它的就是另外一種工作了。
比較需要注意的是
1. 這只是簡單的地圖,畢竟不能跟完整個GoogleMap相比(當然你要寫得超屌也可以)
2. 經過測試,無法去draw載進來的地圖,crossdomain的關係。
3. 一樣是crossdomain的關係,其它的GoogleMap api,像是地址反查這種功能,在web的flash裡也無法直接去做query,可能還是得透過js去做中繼。
4. 這是旁門左道,非必要的話還是請愛用GoogleMap v3。


2011年5月17日 星期二

A*尋路法 (AS3)


前一陣子想寫個遊戲來玩,所以難免一定會遇到在地圖上的人物怎麼找路走的問題,所以看了一下A*。
參考資料是這一篇:
http://www.lihuasoft.net/article/show.php?id=3523
原文是:
http://www.policyalmanac.org/games/aStarTutorial.htm

其實在上面的文章裡面已經把這個演算法說明得很清楚了,所以似乎不太需要再多做說明。
基本上的原則就是:
1. 每次抓取一個點,計算該點附近接鄰點的F值,這個F值是G(移動到該點要花費的移動點數)+H(該點到終點的垂直距離+水平距離總合)。
2. 將被計算的點放到一個open list陣列裡,並將那些點的父節點指向到目前的節點,然後將目前的節點放到close list裡(表示已經走過並計算完成)。
3. 從所有的open list裡抓取F值最小的一個節點出來,重覆1~2步驟,直到計算到終點為止。
4. 取得終點的父節點,再取得父節點的父節點(依此類推),這些被取出的父節點list就是路徑。

我寫了一個AS3的版本,如下(點圖開啟swf):
黑色框框是不可經過的點,點選空白的方框可以設定起始點,再點選另一個方框後就是設定終點,勾選DebugMode之後,一次按一下NextStep來觀看每次尋找路徑的計算過程。點New Map會隨機產生新地圖。
原始檔可在此下載:下載

心得:其實這版的A*似乎是很簡易版的,尋找出來的路徑有時在人類的眼中看起來似乎有點怪異(ex:會繞路),但是基本上對於簡易遊戲來說應該已經很足夠。如果需要更符合自己遊戲的話可以依這個演算法的邏輯去加一些判斷或變數。

2010年9月16日 星期四

FlashPlayer10 SampleDataEvent的音調及音色


大家都知道FlashPlayer10開始可以利用SampleDataEvent事件來產生8-bit的聲音。
雖然我相信真的會去發出8-bit聲音的機會很低(因為真的很難聽),但還是記錄一下如何抓取音程及改變音色。

大略講一下聲音跟波的概念。
基本上音調的高低是由聲音的頻率所決定的,即使是不同的東西振盪空氣造成不同的聲波,只要它們的頻率是一樣的,那麼聽起來也許音色不同,但會是同一個音調,頻率越大音調就越高越尖,頻率越短則音調就越低越沉。
而聲音的大小則是由振幅所決定的,振幅越大聲音就越大聲。
至於聲音的音色則是由波形來決定的。
以一個正弦波來講的話,X軸的部份當波形再次重覆時這段距離是波長,頻率基本上跟波長是倒數,頻率越大波長越短,Y軸的大小就是振幅,它的形狀就是它的波形。


2010年8月17日 星期二

使用ByteArray做一些基本的檔案保護


這一篇的概念很簡單,應該也已經有許多人都這樣在用了。基本上只是一種做心安的保護動作。

不知道為什麼只要遇到bytearray的東西我都覺得很有興趣,雖然這東西說穿了就是直接改binary,沒什麼太大的學問,但是我總覺得光改binary就可以玩很多東西。例如AS3Swf、LivePDF、FZip等等的東西,都是用bytearray玩出來的。

以上說的那些都需要詳讀相關的specification,才能知道哪一種檔要在binary的哪一個地方塞入什麼值,要自己去寫一個的話是很耗時跟耗工的,感謝國外的強者們。

這一篇要說的相反,要保護一個檔案,最簡單的方法就是把它binary搞亂,亂到電腦看不懂,其它人就很難直接把你的檔案拿去用。
因為把binary搞亂了,自然無法直接在電腦上跑,因此如果flash裡要使用的話,就還得先把檔案讀進來重新還原到原本的狀態才可以使用,這是比較麻煩的部份,因為寫code時就要多加一道decode的程序。一般來說會這樣搞的機率不大,swf也都有現成的軟體可以加密保護,但是像mp3這種東西就沒有了。
之前遇到的案子是客戶希望在網站上播放的音樂要保護起來,但是又沒錢買FMS,那怎麼辦?
那就只好把mp3的binary搞亂了。

點下面這裡看範例:
Demo
原始檔下載:
Source


在Encode的地方選檔案並另存後,可以試播看看,基本上會是聽不到什麼的,用其它player播放即使聽得到聲音也只會是一些怪聲音。
在Decode的地方選擇剛剛編碼過的檔案後再另存,就可以發現檔案已經回覆到原來的狀況可以直接播放。
我這個範例裡只是純粹的將mp3每100個binary互相倒置,把binary的順序搞亂而已。
如果要用更複雜的編碼也是可以,同樣的道理而已。

這種方法只是做心安的,只要被人crack到原本的編碼方式,對方又會寫程式的話,也是會被還原回來的。
但我想這樣已經可以擋掉大多數的網路使用者用現成的工具抓取mp3回去聽了。



2010年8月5日 星期四

讀取相片的Exif資訊 - Exif library for AS3



要讀取Exif資訊其實不會很難,直接用bytearray去拆解就可以,但是很費工,今天偶爾看到這個,有人已經寫好現成的類別了,那就省事多了,照片裡有經緯度的話也讀得到,所以直接標在地圖上也是很ok的。
(只不過它的授權是限非商業用途…使用時請注意~)

Demo網頁:
http://shichiseki.jp/as3/exif_info.html

Google Code:
http://code.google.com/p/exif-as3/

有興趣自己寫拆解Exif資料的朋友,下面這個是Exif的格式:
http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html




2010年7月24日 星期六

改善向量動畫造成的效能問題 - AniClip類別


這並不是什麼新概念也不是新東西,而且大家也都知道點陣圖對cup造成的負擔比向量圖來得少。
簡單的說,就是將MovieClip裡每一個影格的畫面都事先用bitmapdata畫起來,再依序播放,這樣雖然吃記憶體,但是很多時候可以減輕cpu的loading,讓整個網站跑起來不會卡卡的。


最早之前是看到bytearray部落格上發表的Banana Slice元件(http://www.bytearray.org/?p=117),真的很久了,兩年前了。當時由於自己本身十分不喜歡用元件的關係,所以想說有空自己再來寫一個類別。結果,人嘛…惰性,時間久了,工作上遇到相關的問題時,也都想辦法先用別的方式解決掉,就一直沒有要把它寫一個真正的類別檔出來。

2010年6月30日 星期三

Facebook OpenGraph的js及Flash AS3類別庫編寫完成 + 心得筆記


Facebook OpenGraph的js及Flash AS3類別庫編寫完成 + 心得筆記

由於Facebook說他們要開始使用New Data Model的Deadline是六月三十號(連結在這http://developers.facebook.com/blog/post/392),因此一些之前已經寫好並且正在線上的iframe、connect app的使用者權限方式要做改變,否則將會發生要什麼資料就沒什麼資料的冏境。雖然說New Data Model仍然可以使用舊的Facebook Connect的permissionDialog來額外讓使用者同意New Data Model的一些新permission,但是我總覺得麻煩,而且希望畢其工於一役,要改就一次改,免得之後又來個什麼東東的deadline,就又要焦頭爛額的去改。

2010年4月28日 星期三

AS3裡出現不Single的Singleton


簡單的來說,就是有時候明明是使用Singleton.getInstance()但取得的instance就偏偏不是同一個。
一樣是一個開發上常會遇到的問題,今天我又遇到了,一下子以為是程式哪裡有bug,後來再看一下發現原來自己忘了會有這種情況發生,所以這次要記得寫下來放在blog上,免得以後自己又犯。

這問題會在這樣的結構下發生:
假設有一個載體A.swf,它會載入B.swfC.swf,然後有一個Singleton Class,就叫Singleton好了。
其中,B.swfC.swf都會使用Singleton.getInstance()來取得實體進行資料交換的動作,但是如果A.swf沒有先進行Singleton.getInstance()的話,B.swfC.swf所取得的實體會是不一樣的,這樣就一點都不single了。



解決方法一種就像剛剛講的,先在A.swf去取得Singleton的實體,但是這樣一來A.swf就永遠沒辦法跟B.swfC.swf進行鬆綁,是挺爛的一個解決方法。

另一個比較標準的解決方法就是在載入B.swfC.swf時順便把LoaderContextapplicationDomain設成ApplicationDomain.currentDomain就好了。

2010年3月4日 星期四

試作TTS (Text-To-Speech) for Flash


今天有朋友分享一個Flash的Text-To-Speech服務,網址在這
http://www.flashrealtime.com/real-text-to-speech-for-your-flash-apps/



這讓我突然想起來,之前因為專案的需求,曾經找過類似的資料,當時由於忙別的東西,所以就停了下來沒把這TTS寫出來。
其實這種TTS的服務有一個遠在天邊近在眼前,那就是Google。

Google並沒有真的「公開」的提供TTS的api給大家用,但是它卻有一個服務是有TTS功能的,而且TTS因為是聲音檔的關係,所以並不需要crossdomain policy就可以播放(當然,如果你要把它丟進mixer裡就是另一回事了)。因此這個服務即使Google沒有置放crossdomain.xml,也可以「不要太明目張膽」的使用。(如果要商業上使用請小心啊... 最好不要亂用...)
喔對,這個服務就是「Google翻譯」。

不過不管是上面那個FlashRealtime的TTS或是Google翻譯TTS,都.不.支.援.中.文!!

但我還是寫了一下powered by Google的TTS。

swf在這裡

整個程式十分簡單,就是直接new一個Sound類別去load一串網址而已:
http://translate.google.com.tw/translate_tts?tl=en&q=whatever you want to speek

就結束了。

因為之前有寫好了mp3切割及串接的類別檔,想說既然是可以直接用Sound類別讀進來,那應該就是mp3格式了吧?
於是順便的Google丟出來的東西丟到我的類別檔裡,但是結果卻發現它不是正確的mp3格式!?
這個問題目前還是無解,挺怪。

另外一個奇怪的問題,在本機寫這個TTS的時候,我送出request一直收到IOError,但是一擺到瀏覽器裡去看就ok!?
我猜Google應該是有做了什麼手腳限定一定要用瀏覽器才給資料吧?

總之就是這樣。

2010年2月24日 星期三

FlashSURF - AS3的圖像辨識


應該是有點累格了,不過是我這幾天看到的東西。
這東西發展起來的話應用範圍會很廣,跟一般的AR不一樣,雖然無法做到3D定位,但是優點是可以隨時選擇想要判斷的圖像,效能上感覺起來也不會太差,在目前flash上的AR都需要marker的情況之下,這一套markerless的技術應該可以產生另外一種玩法。

Demo影片:

Playing with flashSURF from Eugene Zatepyakin on Vimeo.

作者Blog:
http://blog.inspirit.ru/?p=343

AS3的MP3音樂編輯器!?


很久以前剛開始碰byteArray類別時,覺得很多東西可以用byteArray來玩,就曾經去找過mp3的檔案結構來看,然後寫了一個可以把mp3切來切去組來組去的類別檔。
但是後來發現雖然可以切割組合,但是卻遇到了兩個問題:
1. 無法混音,因為mp3裡的音樂資料都是被編碼過的,以AS3的效能來看,即使有codec,AS3也跑不動。
2. Sound類別無法直接讀取byteArray來播放mp3。(這邊指的byteArray是用URLStream或FileReference所讀入的mp3的byteArray,而不是FP 10之後Sound類別透過SampleDataEvent發聲的那種byteArray。)既然無法直接將mp3 的byteArray丟到Sound裡,那麼這個將mp3拆解組合的類別基本上就是一個很雞肋的東西了。
由於遇到這兩個問題,因此我很久就沒再去想起這個類別。

但是最近在查資料的時候,卻意外的發現上述第二點有解法了!這個解法是由Christopher Martin-Sperry這個人所寫的(http://www.flexiblefactory.co.uk/flexible/?p=46),他主要的原理是先在記憶體裡產生一個swf bytecode,再將mp3的資料「注射」到這個swf裡,接著用loadBytes的方式實體化swf,再用getDefinition的方式取出Sound類別,有興趣的可以自己點上面的聯結過去,那個頁面裡有他的類別可以下載,基本上如果我們如果純粹只是要將mp3的byteArray轉成Sound的話,只會用到ByteArraySegment.as、MP3Parser.as及SoundClassSwfByteCode.as這三個類別而已。
發現了這個solution之後,我這兩天就又把之前我自己搞的那個類別拿出來玩了,即使沒辦法混音,想說這樣至少還可以線上切割組合mp3之後馬上播放來聽,而且可以在mp3裡加入一些防君子不防小人的版權保護的東西,於是很手癢的就動手寫了起來。
只不過該怎麼說呢,應該說沒有每天在過年的,我遇到了另一個問題,努力了兩天之後目前無解,情況如下:
現在拆解切割組合以及播放都很ok,歸功於FP10,組好的檔還可以直接存在本機端。但是不管我怎麼修改找問題,切出來的那一塊部份,總是跟我原本想切的那一塊有一點點的時間差,曲子越長誤差越大,可以誤差到約一秒多的時間。

flash點這裡
有興趣的可以自己點過去玩玩看(需要Flash Player10),上傳一個mp3(別太大,因為我又有跑Sound.extract(),所以mp3太大的話記憶體會很耗),拉一個區塊,點「播放被選取的」聽聽看,再點「另存新檔」,再聽聽實體檔,就知道我在說什麼了。

我後來想一想,會不會是我在拆解mp3的byteArray時查找frame header時出了問題?
一般header的格式是4個bytes,以binary的來看的話其格式會是像這樣『AAAAAAAA』『AAABBCCD』 『EEEEFFGH』『IIJJKLMM』的格式,每個字母代表一個意義,而且每個frame的header不一定會一樣,我卻是偷懶直接取header的前兩個bytes來當辨識元,只要符合0xFF 0xFB的我就把它視為是header,我知道光用兩個bytes就來判斷是否是header的做法很冒險,而且運氣差一點的話有些header的第二個byte搞不好根本不是0xFB。
但如果真的是frame header出了問題,我目前說真的還沒想到要怎樣很有效率的依序找出每一個frame的header。

算了,也許這個問題我就只好先擺著了。
如果路過的有高手知道我可能犯錯的問題在哪的話,還請幫忙解答一下~~
感謝!

2010年1月4日 星期一

Facebook Flash Application開發心得(5) - 新版的streamPublish


Facebook的api在之前偷偷的有更新過了一次,但是flash的swc似乎仍然沒有變動,基本上影響不大。
其中發佈到個人塗鴉牆的部份,Facebook打算捨棄原本的template,從原本FB.Connect.showFeedDialog改成FB.Connect.streamPublish,這樣也好,不然每次要弄一個新的format就要去註冊一個新的template,這樣真的很麻煩。由於舊方法目前還可以繼續使用,但是為了將來著想,最好還是將舊有的程式改寫成新方法會比較好。
說是對flash影響不大的原因,因為雖然flash api裡原本就有com.facebook.commands.feed.PublishTemplatizedAction這個類別來處理發佈到塗鴉牆的工作,但是如果使用者沒有去允許這個app擁有完整publish_stream的權限的話(一般來說使用者很少主動允許這個權限),從flash發佈出去的東西是不會主動呈現在使用者的塗鴉牆上的,需要使用者回到自己的塗鴉牆再去點選一次才會真的出現,因此實用性並不大,我也是通常都使用javascript來處理發佈的動作。

因此在這裡記錄一下streamPublish該怎麼處理好了。
新版的發佈動作是藉由javascript api裡的FB.Connect.streamPublish來進行的。
streamPublish(String user_message, Object attachment, Object action_links, String target_id, String user_message_prompt, Function callback, Boolean auto_publish, String actor_id)
這些參數的意義分別為:
1. user_message:要講的話,通常是空的,由使用者自己填寫。
2. attachment:這是一個Object,就是原本的template,用來制定塗鴉牆上顯示的格式,attachment的寫法是重點,待會再提。
3. action_links:塗鴉牆上的連結,一樣是Object型態,通常是[{ "text": "點這邊!!", "href": "http://www.yourdomain.com/xxx.html"}];
4. target_id:要發佈到誰的塗鴉牆上,如果是要發佈到使用者自己的塗鴉牆的話,這個值就要帶空值。
5. user_message_prompt:會出現在user_message之前的一個開頭語,例如『正在想』、『覺得』…etc這些東西。
6. callback:對話窗關閉後會回call的function,如果不打算接受這個事件就不用管。
7. auto_publish:如果使用者已經同意publish_stream的權限且auto_publish是帶true的話,那麼使用者將不會看到有對話窗跳出來,訊息會直接出現在塗鴉牆上。(個人十分討厭這種作法)
8. actor_id:這好像是粉絲頁面用的,可以選擇直接發佈到粉絲頁,但前提是這個使用者是該粉絲頁的管理者。


看起來是很簡單明瞭了。
所以主要的重點還是在attachment。其實attachment跟以前的template差別不大,分別只在於以前的template先制定好了『格式』,現在的attachment則是隨時可以帶入不同的Object,比較活。
官方wiki對於attachment的解說請參考這個網址
http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29

通常attachment會具有以下幾個properties:
1. name:就是主旨。
2. href:主旨點下去會連結的網址,通常是app或是某活動、官網的網址。
3. caption:跟name不一樣,也跟下面的description不一樣,這個caption通常會寫出誰誰誰做了什麼事,在caption裡加上{*actor*}的話這一串字串就會自動被取代成使用者的名稱,例如:『{*actor*}剛剛做了心理測驗』,出來的結果就會是『Jason剛剛做了心理測驗』
4. description:嗯,這個就是一大堆文字了,可以當成作文去寫,通常facebook裡那些心理測驗在解釋測驗結果的那一大堆話就是塞在這個屬性裡。
5. properties:這屬性我還沒實作過,功能還不明,跳過。
6. media:除了description以外最多人用也最有興趣的就是這個屬性了,這是一個陣列,定義了所有會出現的圖或flash或聲音,全都塞在這個陣列裡,陣列裡放的是Object,每一個Object會有三個屬性:type,src,href。type定義是什麼媒體,有image、flash及mp3三種,src則是這個媒體所在的網址,href就是點了之後會連到哪。所以media應該會是類似這樣的型態:[{'type': 'image', 'src': 'http://icanhascheezburger.files.wordpress.com/2009/03/funny-pictures-kitten-finished-his-milk-and-wants-a-cookie.jpg', 'href': 'http://icanhascheezburger.com/2009/03/30/funny-pictures-awlll-gone-cookie-now/'}, {'type': 'image', 'src': 'http://photos.icanhascheezburger.com/completestore/2009/1/18/128768048603560273.jpg', 'href': 'http://ihasahotdog.com/upcoming/?pid=20869'}]
7. 剩下的像comments_xid及自定義屬性,好像都是要用再更深入的機制上才會用到的,目前我也沒實作過,因此一樣跳過。
基本上attachment最常被使用到的就是這幾個屬性了,差不多瞭解了之後就可以很快的發佈屬於自己的塗鴉牆。


接下來,如何從Flash去呼叫FB.Connect.streamPublish,不用說一定是透過ExternalInterface,需要注意的是,因為attachment是一個Object,我試過直接用Flash丟Object出來給js,結果是失敗的,雖然js也是收到一個Object,但是其實這個Object已經不是原本的Object了,所以要嘛就是把要發佈的attachment先在js裡寫好,但是這麼一來整個code又變得很不活,也無法類別化,所以我都會改採另外一種方式:JSON。
在Flash裡,先產生相對映的Object,然後藉由Adobe as3corelib裡面com.adobe.serialization.json的package,JSONEncoder類別將Object轉換成JSON的字串後,用字串的方式傳遞給js,js裡再還原成Object就可以了,js裡字串還原成JSON的方法可以直接用eval,或是使用JSON.js來做parsing的動作(JSON.js可在此下載http://www.json.org/js.html)。
這樣做的好處是可以在flash裡動態的隨時產生不同的attachment,視不同的狀況發佈不同的塗鴉牆,比較靈活,js也不需要因為每個專案而再重新編寫。

2009年9月3日 星期四

Facebook Flash Application開發心得(4) – Iframe的架構下取得Session


相對於FB Connect在取得Facebook登入Session值的複雜度,Iframe架構下的Application在這方面反而方便許多。
前一篇已經有說到Flash api裡的FacebookSessionUtil在FB Connect的情況下會有bug,但是在iframe之下卻可以運作得非常順利。其中很大的原因,是因為iframe的架構原本就是附屬在Facebook裡的,登入的狀況都由Facebook先處理掉了,Facebook才會開始載入iframe,也就是說user很少會在還沒登入的狀況之下就接觸到你的iframe application,因此我們可以少掉一部份處理尚未登入的情況,但如果user真的有辦法在未登入的情況下進入我們的iframe application(會有,在某些情況下會發生),我們只要直接把頁面導回給Facebook處理就好,等登入後Facebook會自動再幫我們導回來(很方便吧!)。這個利用Facebook來做導向的機制也會發生在user第一次進入應用程式的時候的那個授權頁。
另一個很重要的原因,正由於iframe架構是由Facebook所載入的,因此Facebook「很貼心」的順便帶了一些十分有用的資訊放在url後面的變數裡給iframe,這一大堆的資訊裡面就包括了Session值。

既然知道了這很重要的兩點,就來實作吧。
首先當然是針對javascript下手。

依照剛剛所說的兩個重點,一開始先要取得的就是Facebook帶給iframe的所有必要參數。

上面這段code最重要的部份就在於取得所有網址列後面的變數並放在flashVars裡。
另一個重點就是fb_sig_added變數的取得,它代表了user是否已經授權使用你的application,如果是0(尚未登入的情況下好像也會是0),我們就把整個頁面導向到http://www.facebook.com/login.php?api_key=" + api_key + "&next=index.htm&v=1.0&canvas=1的這個網址,網址列後面的參數api_key就是你的api_key,next指的是授權後要再導回來的頁面,我習慣是再導回index.htm。(有興趣的人可以觀察一下,事實上Facebook如果是在你已經登入的情況但沒授權的時候是會導到另外一頁的,不過next也會跟著被帶過去,所以我們只要直接全交給login.php去處理就好)
當一切都就續時,我最後呼叫一個initFB的function,這個function裡我就開始初始化facebook javascript api。

初始化Facebook javascript api的部份,跟FB Connect不一樣,我這次採用FB_RequireFeatures來做為初始化的點,因為我很確定user已經是登入狀態,所以一開始就初始化所有該用到的東西。
而且如果我沒記錯的話(說真的印象有點久遠了),使用FB_RequireFeatures的話,Facebook會強制在你的appilication外層加上它的外框(上面的header及下面的footer),所以如果你是直接輸入你application實際的網址的話,遇到FB_RequireFeatures時還是會Facebook框起來。
因此之後有時候,我們將會遇到iframe及FB Connect同時應用在一個application的情況中,也就是說即使在iframe的情況之下,有時還是要用到FB Connect,這也是我先介紹FB Connect的原因之一。(如果我以後還會繼續寫下去的話)
至於FB_RequireFeatures裡所帶的一些值的意義,可以去官方的wiki上看,基本上我也沒有實際的去比較這幾個之間差多少,所以我就乾脆直接初始化三種我覺得可能會用到的。
通常這個function這樣應該就ok了,不需要改寫,直接拿去用就好。
等到一切都就緒後,就呼叫initFlash這個function來載入flash,這function我就不寫出來了,反正就是用swfObject載入就是了 (記得要將flashVars帶進去)

接下來就是Flash裡的code了。
既然已經說了FacebookSessionUtil在iframe裡很好用,就直接大方的用了吧。

基本上,user算是通過層層關卡才會到達flash這一層的驗證:Facebook本身一層,javascript有兩層驗證,第四層才會是FacebookSessionUtil,所以基本上在上面的code裡使用FacebookSessionUtil.login()是用於開發時直接在本機端跑的情況下,否則應該都是直接verifySession就可以取得登入uid了。
ps:如果是在開發的時候,api secret值需要寫入,否則無法登入,這就是Facebook建議我們不要把api secret值寫死在code裡的原因,因為一但被取得,其它cracker就可以利用本機端寫程式來破壞你的application,所以記得上線時要把api secret值從code裡拿掉,FacebookSessionUtil自動會從loadInfo裡所有的flashVars取得它該使用的secret值。

2009年8月20日 星期四

Facebook Flash Application開發心得(3) - 由Flash Api從Facebook取得資料


當最煩人的session問題解決之後,就該試著跟Facebook要點資料來瞧瞧了。
這一篇會大概解說一下Flash api如何跟Facebook索取資料的動作。
基本上Facebook Flash api在這方面的流程很簡單,其實就是先產生一個「命令」然後把這個命令丟給Facebook類別往外post出去就好,要做什麼動作就去產生相對應的命令類別就可以了。
所有的命令類別都繼承com.facebook.net.FacebookCall,有興趣的人可以直接去說明文件看這個類別,它就有列出所有它的子類了。
說明文件在這邊:
http://facebook-actionscript-api.googlecode.com/svn/release/current/docs/index.html
幾乎所有的命令類別都是在com.facebook.command的package裡。

相對於發出命令的簡易,Facebook Flash api在接收資料時的繁複卻是不可思議到讓人惱怒。有時一個命令發出後,收到的資料型別居然要層層解析,動輒用到四五個類別,再加上說明文件的陽春,所以往往需要不斷的一直trace,最後才能得到你想要的資料。
這個部份待會的code就會見識到…。

接下來,我們繼續接著上一篇的去修改,Html的部份就不用動了,這次沒動到javascript,所以只改ActionScript。
我們這次要從Facebook取得自己的個人資料,因此在上次的connectHandler裡驗證成功後馬上執行這個動作:
使用到的是com.facebook.commands.users.GetInfo;這個類別。



GetInfo這個類別是用來跟Facebook索取會員資料用的,它可以允許一次索取很多人的,因此第一個參數是會員uid組成的Array。然後也可以指定要索取哪一些欄位,這些欄位的定義都在GetInfoFieldValues這個類別裡的靜態屬性,一樣是Array的形式,如果你要索取所有的欄位,那就直接帶入GetInfoFieldValues.ALL_VALUES就好,它本身就是一個Array型態。

接下來就是要處理事件回傳的結果:



開發Facebook application時,一直trace回傳的FacebookEvent會是一個好習慣…。FacebookEvent的形態基本上會是這樣:
[FacebookEvent type="complete" success=true data=[object GetInfoData] error=null]
type就不用講了,要注意的是success及data。
success就只有true或false,通常都是true,除非你跟Facebook之間的connection斷了,那success就會是false然後error會有東西。
data則是主要的資料,data會依照你命令的種類不同而有不同的資料型態,理論上命令的類別都是在com.facebook.command.xxxx的package裡,相對應的資料型態類別則會放在com.facebook.data.xxxx的package裡。
也就是送出命令時是去command裡找,接收到事件時就回來data這邊找。

這次的命令是com.facebook.command.users.GetInfo,收到的資料是com.facebook.data.users.GetInfoData。
但是其實討人厭的就在這邊,光一個GetInfoData還不夠,真的資料是存在GetInfoData類別裡的userCollection屬性 (com.facebook.data.users.FacebookUserCollection類別),所以只好不斷的轉型別把它轉成陣列,最後陣列裡儲存的是一個一個的com.facebook.data.users.FacebookUser型別的資料…。
因此光解析個人資料就要動用三個型別… 加上發出命令的類別的話一共是五個,老實說,說明文件夠好的話這都還ok,偏偏不是,所以開發Facebook application的話一直trace會是一個好習慣。
當熟悉它的運作方式後,十分建議自己寫一些類別來整合簡化這些步驟。


如果沒意外的話,當登入後就可以看到自己的名字跟照片顯示在畫面上了。

下一篇
Facebook Flash Application開發心得(4) – Iframe的架構下取得Session

2009年8月19日 星期三

FP9操作影格造成child MovieClip裡的動畫暫停的bug


這問題是奶綠前兩天發現的。
通常我們很習慣在操作動畫時,有時為了要控制快轉倒轉,常常會使用nextFrame()及prevFrame(),而且通常是配合Event.EnterFrame來使用。
但是奶綠發現,如果這個MovieClip有一個child MovieClip自己裡面有動畫在跑的時候,這樣的操作會造成這個child MovieClip的動畫「停止」,等到parent不再受到nextFrame()及prevFrame()的命令時,child MovieClip才會繼續跑它的動畫,因此就造成了不同步的狀況。

我自己試驗了一下,其實child MovieClip裡的動畫不是真的停止了,而是「跑得比較慢(應該說非常慢)」而已。
我猜可能是因為nextFrame()及prevFrame() (gotoAndStop()也一樣)所花掉的效能讓Flash Player無法同時處理child MovieClip的影格,因此造成child MovieClip的動畫delay。
目前似乎無解。

但是奶綠發現Ticore之前有提到在Flash Player 10之下這個問題就不會再發生。
也許是Flash Player 10有發現這一點並做了修正了吧?

Facebook Flash Application開發心得(2) - FB Connect - 接上Flash


使用FB.init初始化Facebook之後,除了要藉由javascript的api來應用Facebook的一些功能以外,其實最重要的就是要取得Facebook的登入session資訊。
由於Facebook的判別登入權限是由很多參數所記錄及驗證的,flash的api也需要這些參數來進行資料的傳遞,因此取得這些參數是很必要的。
這些參數裡有兩個值是最重要的,分別是session_key及secret,前者是此次使用者登入的session值,後者則是facebook動態產生的secret值,這個secret值跟Application設定時所看到的secret不太一樣,基本上Facebook官方建議我們不要將Application設定畫面上的secret值直接寫在程式裡,因為很容易被有心人破解取得,所以Facebook的javascript api在進行驗證時會自己從Server取得一組由Facebook動態產生的secret來使用,但是Flash api就辦不到,必需要將secret寫死在flash或是flashvars裡,這也就是要使用javascript api來進行登入動作的原因之一。
另一個使用javascript api的原因就是session的取得,雖然官方的flash api的文件裡建議我們使用com.facebook.utils.FacebookSessionUtil類別來統一處理session,文件裡說FacebookSessionUtil會自己判斷是該使用DesktopSession、JSSession或是WebSession,但是我發現FacebookSessionUtil是有bug的,即使在網頁上跑flash,它仍然會使用DesktopSession來做為session的處理類別,但是這麼一來一但當頁面被refresh時,flash將會失去session的相關處理能力,需要再次重新登入一次,對使用者來說這會變得非常的不友善。
雖然說FacebookSessionUtil有這種缺點,但是它在iframe架構的application裡卻可以運作正常且很好用,因此關於FacebookSessionUtil的部份就留到iframe的時候再講,目前為止在FB connect的部份我將會直接使用WebSession。

現在要將flash放入到FB connect的application裡,會延用到上一篇的東西,但稍作改寫,由於我假設我們是要開發全Flash的application,因此我也將登入Facebook的按鈕從html裡搬到flash裡。

整個主要程式的運作流程會是:
1. 載入swf。
2. Swf呼叫javascript進行FB.init。
3. Javascript去跟Facebook驗證使用者是否已經登入。
4. 若是已經登入,javascript取得必要的session_key及secret,並將這兩個值傳給swf。
5. Swf依照這兩個參數進行WebSession的初始化。

一、 HTML上javascript的改寫:

應該很直覺,不用再多做解釋。

二、 Flash裡的處理:
因為code不算多,直接全部po出來,加上註解。


三、 其它解說:
在Flash裡,我捨棄了使用Flash api裡的login method,而決定呼叫javascript來處理的原因,是因為如果沒有將application secret傳出的話,facebook.login()不知為何一直無法正常運作,但是如果我把application secret寫入,那就又失去了安全性,因此我才又將登入動作交還給javascript處理。
FB.Connect.requireSession有兩個參數,第一個是登入後的callback function,由於我們已經實作了ifUserConnected,因此這個function可帶入null,第二個參數是決定是否要另開一個視窗當登入窗口,通常這個參數可以忽略,在純html的狀況之下呼叫FB.Connect.requireSession一定會另開,不過如果這個動作是由flash所觸發的話,預設值會是直接蓋在頁面上,可是flash的wmode沒有設成transparent的話flash反而會蓋在登入畫面之上,因此我特別將第二個參數設成true來強迫它另開一個視窗出來。
更正:經過實驗,似乎只有在IE才會跳出新視窗,FireFox裡好像不管怎樣都是跳lightbox...

這個程式的整個流程還沒有完全的完整,但是已經有考量到使用者是先登入再到這頁面或是來到這頁面才進行登入動作的兩種狀況,在一般自己的網頁上來處理應該是足夠了,但是還沒處理到使用者半途登出Facebook的情況,這一點可以事後再繼續補足,至少最麻煩的session取得已經解決。

接下來我也許會先介紹幾個從Facebook取得資料的類別,然後就可以再回過頭來介紹iframe的架構,其實iframe與FB connect最多最大的差別就只在於session的取得,其它的都大同小異。

下一篇
Facebook Flash Application開發心得(3) - 由Flash Api從Facebook取得資料

2009年7月17日 星期五

DisplayObject上層擋到下層滑鼠事件問題


有一個常遇到但是一但遇到老是不求甚解的就把它解決掉的問題,這幾天在處理一個案子的時候又遇到了。
在AS3裡,位於上層的DisplayObject會阻擋滑鼠事件,所以會導致較下層的物件無法接收到滑鼠事件。這理論上看起來是合理的,只是問題的發生點通常都是在我們常會忽略掉的地方,那就是透明圖層。
設計師們常常會利用PNG的透明背景來處理一些效果,而我們也常常忘了這個PNG其實是一個方形的存在,因此就會在Runtime時遇到明明看起來沒東西的地方但是滑鼠事件就是不會work的狀況。

Ok,解決的方法就是將擋到的DisplayObjec的mouseChildren及mouseEnabled設為false。通常這樣也都可以解決問題。
但是這次我遇到的狀況比較不一樣,才發現事實沒這麼簡單。
我這次的狀況是,場景上一個MovieClip(簡稱MC_A)裡面有動畫,其中有一個按鈕。但是很不幸的這些動畫擋到了場景上的另一個MovieClip(簡稱MC_B)裡的按鈕。(請注意MC_A及MC_B是位於同一個DisplayObjectContainer裡)
很直覺的我用一個迴圈將所有MC_A裡除了按鈕以外的DisplayObject的mouseChildren及mouseEnabled都設為false,感覺這樣就可以解決問題,但是很遺憾發佈後MC_B還是一樣被MC_A的一些東西擋住(這些東西我都已經設成mouseChildren及mouseEnalbed為false了)。
於是我做了一些實驗,我在MC_A裡把MC_A的按鈕都放到最低的層級裡,把動畫都拉上來,如果我沒跑迴圈去將動畫的mouseChildren及mouseEnalbed設為false的話,MC_A裡的按鈕及MC_B裡的按鈕都不能work。但如果我有跑迴圈,那麼只有MC_B裡的按鈕會無法work。
有些人也許已經知道是怎麼回事了。

照這種情況看起來,MC_A裡面的DisplayObject其mouseChildren及mouseEnabled屬性只會在MC_A這個DisplayObjectContainer裡生效,對於MC_B這個與MC_A存在於同樣一個層級的物件來說,MC_A的「滑鼠有效範圍」仍然是整個MC_A的所有範圍。
因此在MC_A裡不管怎樣的最裡面的mouseChildren或mouseEnabled做操作,對於MC_A以外的物件來說,它們只認識MC_A自己的mouseChilren及mouseEnabled。

在這個案例裡,由於我的MC_A裡還有按鈕,所以我不可能將整個MC_A自己的mouseChilren及mouseEnabled設為false,因為這樣一來我MC_A裡的按鈕也跟著廢掉了。
最後我才知道我忽略了一個很簡單的解決方法,那就是直接將MC_A的hitArea屬性設為按鈕實際的感應區域就好了。這樣子對MC_B來說,MC_A的實際「滑鼠有效範圍」就會被改變了。

2009年6月17日 星期三

Facebook Flash Application開發心得(前言)


Facebook的API很久以前就有了,嗯。
不過前一段時間,就在Adobe跟Facebook正式宣佈要一起建構什麼新型態的社交平台之後,終於有了CS3版本的swc。(之前都是Flex版的)
基本上我仍然十分習慣在CS3之下工作,還不是很喜歡打開Flex。所以一看到有CS3的版本後就開始把玩了起來。

說真的,目前為止,我個人覺得Facebook的API說明文件寫得真的不是很好,有太多東西需要開發者自己去摸索。其實我可以理解Facebook針對client端語言的一些限制,畢竟權限以及預防API的被濫用是他們很大的一個課題,只是這些東西我都是要google好久才發現原來我必需要繞一條路去跑。

另一個部份是Application的設定問題,倒是也花了我不少時間才搞清楚什麼東西要怎麼設。

接下來應該會花幾篇的功夫記錄我這幾天在Facebook上開發flash application的心得。Adobe官網上的文章在某方面可以一看,但是更進階的部份卻更新十分緩慢,O’Reilly Inside RIA裡Mirza Hatipovic寫的幾篇文章倒是給了我一些方向,有興趣的可以先從他的文章開始。
http://www.oreillynet.com/pub/au/3675
他似乎是預計要寫二十篇來做教學,但是似乎很不巧的在他寫到一半時Facebook for Flash的API有了一些改變,但是我想基本原理不變。


至於我的部份,純粹會是記錄我在實際設定以及執行上遇到的一些問題及解決方法,一來讓我可以自己將來回頭再來翻閱,而來如果有人也需要這些資訊的話希望可以幫上一點忙。

之後新增的有關Facebook開發的文章將會列舉連結在下面:
-----------

Facebook Flash Application開發心得(1) - FB Connect - how to start
Facebook Flash Application開發心得(2) - FB Connect - 接上Flash
Facebook Flash Application開發心得(3) - 由Flash Api從Facebook取得資料
Facebook Flash Application開發心得(4) – Iframe的架構下取得Session
Facebook Flash Application開發心得(5) - 新版的streamPublish

2009年6月11日 星期四

Loader跨網域讀取圖檔,以及讀取Picasa的限制


Loader在跨網域讀取圖檔基本上是不需要crossdomain policy的,只是純粹當個乖乖的DisplayObject是沒什麼問題的,但是如果一但需要使用BitmapData對它做draw的動作時,flash player的安全性原則會封鎖這個動作。

這個問題很常見,之前也遇到過好幾次,但是一直沒有白紙黑字記下來,所以搞得自己常常忘記。
基本上要避開這個問題會需要用到LoaderContext這個類別。

通常我們在使用Loader讀取圖片或swf時:
public function load(request:URLRequest, context:LoaderContext = null):void

第一個參數URLRequest一定要的,第二個LoaderContext一般來說我們常常省略不下。不過現在要解決上述的問題時,我們就需要用到它了。
將LoaderContext. checkPolicyFile設成true再帶入就ok。但前題是遠端網域那邊要有允許你crossdomain才行 ,不然的話依舊沒輒。


順帶一提跟Picasa有關的,當Loader在讀取Picasa相簿裡的大圖時,很奇怪的事會發生。
開發時都很正常,丟到FireFox上也可以跑,但是一但用IE(測試的版本是IE 6)就怎樣都會遇到http 404,可是網址copy出來直接用IE開就又讀得到。
後來才知道在IE裡Loader如果去讀Picasa裡尺寸大於800 pixels的圖檔就會這樣,這似乎是Picasa的限制,只對外提供最大尺寸為800的圖(只是我不懂為什麼只有IE會這樣...)。

總之要解決這個問題就只能在Picasa大圖的網址後面加上「?imgmax=800」就好了。

2009年6月9日 星期二

URLLoader取得的bytesLoaded有可能大於bytesTotal!?


無意中發現了這一個很奇怪但是我目前為止無法解釋的現象。
我現在並不認為這是URLLoader的「Bug」,因為我覺得這種情況是有點複雜的,也許並不完全是URLLoader的問題。


情況如下:
今天手癢想寫個類別把我自己在Picasa相簿裡的一些照片資訊取出,讓自己以後可以方便編寫相簿瀏覽的介面。
因為在本機開發還不需要顧慮到crossdomain的問題,所以一開始我是直接取用Picasa的RSS,起初都還挺順利的,RSS的解析也都寫得差不多了,但是當我改讀另一個照片比較多的相簿時,就發現要將URLLoader.data給轉型成XML時一直出錯。
這個RSS的URL如下:
http://picasaweb.google.com/data/feed/base/user/jason.tseng76/albumid/5243914003216812001?alt=rss&kind=photo&hl=zh_TW

第一個反應就是,會不會是RSS的檔太大了? 導致RSS在還沒被完全讀取完全之前URLLoader的COMPLETE事件就被觸發了?
所以我就多監聽了一下ProgressEvent.PROGRESS事件,結果就很有趣了,trace出來的結果是bytesLoaded的值居然比bytesTotal還大。就看著ProgressEvent硬生生的把破表的值給丟了出來。
我也試過把URLLoader.data的值給trace出來,但是可能真的太大了,每次trace就每次當。

於是我也只能再次懷疑是不是檔頭或什麼的東西造成了URLLoader的判別出錯,直接把URLLoader.data轉型成XML出問題恐怕一定是整個資料讀取不完全(或是讀到多的東西!!?)。
所以我只好改走Picasa專門提供給Flash使用的RSS(有crossdomain policy的路)。
http://code.google.com/intl/zh-TW/apis/picasaweb/reference.html#Flash

轉換過後原本RSS的URL會變成這樣:
http://photos.googleapis.com/data/feed/base/user/jason.tseng76/albumid/5243914003216812001?alt=rss&kind=photo&hl=zh_TW

再try一次。
過了!
但是bytesTotal永遠都是顯示為0…。
好吧… 只能說至少work了。
至於為什麼bytesTotal會是0? 我想這已經有點超出我知識的範圍了…。