Category Archive: ActionScript

[AS][iPhone]AIR for iOSにおいてANEファイルを含めたビルドをするには、”mac版”のFlashBuilder4.6一択という残念な現実(GUIを頼りにする場合)

タイトル通りなんですが、そこにたどり着いた経緯をダラダラ書いておきます。

AIR for iOSで作ったアプリはマナーモード(正式にはサイレントスイッチ)ON状態でも音がなってしまいます。
(Flashからはそこを制御するAPIが用意されていないようです)
が、NativeExtension for Air(ANE)という機能が用意されており、自分で足りないAPIを補うANEを用意することでAIR for iOSでもできることを拡張することができます。

ANEはネイティブコード(Objective-Cとか)で作れますが、めんどくさい。
サイレントスイッチを何とかするANEもきっと誰かが作ってくれてるはず。

と、ググってみるとあっさり見つかりました。
StickSports / ANE-Silent-Switch

ソースからANEファイル、テスト用のコード(testフォルダ)まで入っていました。
とりあえずこのテストコードを実機で試してみよう!
と思ったら落とし穴いっぱいだったというお話です。


・はまりポイント1

testコードビルド方法がよくわからず。
READMEには

Antビルドスクリプトのテスト/ build.xmlをビルドし実行します。これは、テストの/ binフォルダ内のテストのIPAを作成します。

Antでビルド?なにそれおいしいの?
FlashDevelopとかFlashCS6でできないの?
できるだけ慣れてるやつでやりたいです。
まずはFlashDevelop。


・はまりポイント2

FlashDevelopでaneファイルを紐付けたビルドがよくわからず。

aneを使ってのFlashDevelopでのビルド例としてこの記事を教えてもらったけども、じっくり読む前にとりあえずFlashCS6にはaneの設定が増えているからきっと簡単にできるだろう、とFlashCS6に逃げる。

2012/06/18 追記
FlashDevelopでaneビルドできましたので記事書きました。


・はまりポイント3

FlashCS6でaneファイルを正しく設定していざパブリッシュ。
すると、こんなエラーが大量に出現。

ld warning: unexpected srelocation type 9

ググってみるといろいろ記事がでてきてiOSのsdkがなんちゃらとか、コマンドラインからもろもろ設定したADTを叩くとかさくっと解決できそうな方法は見つからず。
一番簡単そうなのは、MUSHIKAGO APPS MEMO さんの記事で、FlashBuilderなら「Apple iOS SDK」という項目に最新のiOSのSDKを指定しておけば大丈夫っぽいとのこと。

だがしかし、FlashCS6にはそのような項目がどこにも見あたらず・・・
せっかくaneファイルを簡単に設定できるようになってるのに世の中に落ちてるものを簡単に使えないんじゃ意味無いじゃん・・・
というわけで、解決策のあるFlashBuilderに逃げることに。


・はまりポイント4

Xcode4.3から最新のiOS5.1.sdkフォルダをまるっとwindowsにコピーしてさぁ設定しようかと思ったら。

Win版のFlashBuilderではこの機能が使えないとな・・・
値段一緒なのに。。Windowsユーザーをバカにしてんの。。

というわけで、macのFlashBuilderで試してみたらようやくと実機でテストが正常に動きましたよ、と。

・結論
GUIを使ってANEを含んだビルドしたい人は、とりあえずmac版のFlashBuilde4.6を使うしかないみたい。
とりあえずバージョンアップなりでもうちょっとなんとかしてくださいよ、アドビさん。

[AS]FlashPlayer11で、FMS接続でのNetStatusEvent.NET_STATUSをリスナーしてるときのNetStreamからのdispatchの挙動が変わったっぽい。

FlashPlayer11(以下FP11)とFlashPlayer10(FP10)で、FMSの動画を再生する際に、NetStreamでNetStatusEvent.NET_STATUSイベントをaddEventLinster登録したさいのイベント配信のタイミングがFP11とFP10で異なる現象に遭遇した。

具体的には以下をドキュメントクラスとしてパブリッシュしたswfを再生した場合に、traceされる結果がFP11とFP10で異なった。

package 
{
	import flash.display.Sprite;
	
	public class FMSTest extends Sprite 
	{
		public function FMSTest() 
		{
			//FMSサーバーへのパス
			var dirPath:String = "rtmp://hoge.hoge.hoge.hoge";
			var filePath:String = "mp4:hoge.mp4";
			addChild(new LoadMovie(dirPath, filePath));
		}
	}
}
import flash.display.MovieClip;
import flash.events.NetStatusEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;

class LoadMovie extends MovieClip
{

	private var nsUrl:String
	private var nc:NetConnection;
	private var video:Video;

	public function LoadMovie(ncUrl:String, nsUrl:String, w:uint = 320, h:uint = 320 ) {
		this.nsUrl = nsUrl;
		//ネットコネクションを作る
		nc = new NetConnection();
		//ネットコネクションの接続状態を調べるリスナー
		nc.addEventListener(NetStatusEvent.NET_STATUS , onNcStatus);
		//ネットコネクションを接続する。
		nc.connect(ncUrl);
		nc.client = new Object();
	}
	
	//ネットコネクションの状態でいろいろ出力される
	private function onNcStatus(e:NetStatusEvent):void 
	{
		switch (e.info.code) 
		{
			//もし無事に接続されたら、
			case "NetConnection.Connect.Success":
			initStream();
			break;
		}
	}

	//ネットストリームの初期化処理
	private function initStream():void {
		//ネットコネクションを作る
		var ns:NetStream = new NetStream(nc);
		//ネットコネクションの接続状態を調べるリスナー
		ns.addEventListener(NetStatusEvent.NET_STATUS, onNsStatus);
		//読み込みの時間を設定
		ns.bufferTime = .1
		//メタデータ取得用のオブジェクトを設定
		ns.client = new Object();
		//videoインスタンスを作成
		video = new Video();
		video.attachNetStream(ns);
		addChild(video);
		//再生開始
		ns.play(nsUrl);
	}

	private function onNsStatus(e:NetStatusEvent):void {
		trace("onNsStatus : "+e.info.code);
	}
}

再生した際のtraceの出力結果を以下に示す。
FP10

onNsStatus : NetStream.Play.Reset
onNsStatus : NetStream.Play.Start
onNsStatus : NetStream.Buffer.Full

FP11

onNsStatus : NetStream.Play.Reset
onNsStatus : NetStream.Play.Start
onNsStatus : NetStream.Buffer.Empty
onNsStatus : NetStream.Buffer.Full

FP11では、NetStream.Buffer.Emptyが追加されている。
再現性は100%ではないが、6割~9割位の割合でNetStream.Buffer.Emptyイベントを取得するっぽい。


で、再生開始のタイミングでNetStream.Buffer.Emptyイベントがでることで何が困るかというと、FMSでは動画の終了のタイミングを取るのにNetStream.Buffer.Emptyを使うことがあるからだ。
参考:RTMP接続のランダム再生で最後の数秒が途切れてしまう!


再生開始のタイミングでNetStream.Buffer.Emptyを出したくない、どうしたもんかと思ってたら解決策っぽいのが見つかった。
Netstream.bufferTimeを多めに取ってあげるとよいようだ。
具体的には上記コードの59行目

ns.bufferTime = .1; //0.1秒
//↓0.1秒を2秒にする
ns.bufferTime = 2; //2秒

これでNetStream.Buffer.Emptyはtraceされなくなった。
とりあえず、同じ現象に遭遇してしまった人の参考になれば幸いです。

[iPhone]3GSから4sに機種変更するとき「実質無償機種変更キャンペーン」を利用するメリットとデメリット

以前書いた記事
「[iPhone]契約2年を迎えたときに、2年契約(キャンペーン)を更新する or しない のメリットとデメリット」
のオマケ的なお話。

本日iPhone4sに機種変更してきました。
現在softbankは以下のようなキャンペーンを実施しています。(11/30まで)

「実質無償機種変更キャンペーン」

これは、ざっくり以下の内容。

  • 使用中の3GSの端末価格の支払いが済んでない場合はその分はチャラにするよ
  • 支払いが完了してる人(ボクはここに該当)でも6000円キャッシュバックするよ

普通に考えると利用しない手はないのですが、ボクは今回このキャンペーンをあえて使わなかった。


その理由は、(前回の記事の内容とも関係するけど)
このキャンペーンを利用するといわゆる2年縛り状態になってしまう、ということ。
つまり特定の期間外でsoftbankを解約した場合に約1万円の違約金が発生してしまう。

前回の記事にも書いたようにボクは現在softbankの2年縛りの呪縛から開放されている状態だった。
目先の6000円を追うよりも、いつでも解約できるという精神的な安定を望んだわけだ。
もちろん解約しないでずっと使うよ、って人はキャンペーンを利用したほうが得だろう。
ただ個人的に、電話会社のプランは日々変化しつづけていて、それが消費者にとっては改悪の場合もあるわけで、新規で契約する場合にはそちらしか選べないけど、既存の契約者が旧プランを維持できるんであればそれを利用しつづけるのも手かな、と。
一度プラン変えちゃったら、もう元には戻れないと思うし。


p.s
今回アップルストアで機種変更したのだけど、対応してくれた店員さんはとても親切でボクの過去3ヶ月の通話記録からもっと安くなるようなプランを提案してくれた。
具体的には全然通話してなかったから、Wホワイトよりホワイトの方がお得ですよ、と。
ありがたくその提案に従ったけれども、さすがに上記キャンペーンについての細かい話はなかった。
(ボクが2年縛りのかかってない状態かなんて知らないだろうしね)
こちらから質問することで丁寧に教えてくれた。
なんでも直接聞いてみるのが一番だね。

[AS]AS3での、TextField.htmlTextでつかえる、HTML,CSSのまとめ

以前作ったもの。
ネットで検索できるようにメモ。

[AS]Progressinで「at jp.progression.casts::CastPreloader/set contextMenu()」なエラーがでたときの(とりあえずな)対処法

Progressionを使ってこんなエラーに遭遇。

TypeError: Error #1009: null のオブジェクト参照のプロパティまたはメソッドにアクセスすることはできません
at jp.progression.casts::CastPreloader/set contextMenu()
at com.courservector.flashbug::Profiler/allCompleteHandler()


詳しく検証してないからわかってることだけとりあえず箇条書き

  • FlashPlayerがデバッグプレイヤーのときだけ発生
    (普通のプレイヤーでは発生しない)
  • FireFoxで、かつFireBugとFlashBugが入ってるときに発生
  • FireBugとFlashBugを無効化するとエラーはでなくなる
    (このときPCを再起動する必要があった)
  • 無効化してからもいちど有効化したらエラーがでなくなった
    (わけわかめ)

再現の規則性がよくわからんのでブラウザ(FF)とアドオンのバグなんじゃないかと推測。


とりあえずProgression側での対処法も書いておく
CastPreloader.asの431行目にあるsuper.contextMenu = menu;をコメントアウトすればOKっぽい。
(右クリックでProgressionのライセンス表記もでるし、とりあえず大丈夫っぽい)

抜粋したコードはだいたいこんな感じ
[

]
//CastPreloader.asの428~431行目あたり

// ContextMenu を作成する
var menu:ContextMenu = new ContextMenu();
menu.hideBuiltInItems();
//■↓ここをコメントアウトする
//super.contextMenu = menu;

[AS]画像をゆっくり動かす方法 その2

以前にも書いた内容についての補足です。
画像をゆっくり動かす方法

上のリンクを読んでもらうとわかるのですが、
画像をゆっくり動かすにはbeginBitmapFill()の第2引数のMatrixを使って位置を変更しながら毎フレーム描画する、
ということだったのですが、これだとあまり直感的でなくてちょっとめんどくさい。

↓のサイトを観たらもっと簡単にできました。
にゃあプロジェクト|[AS3.0] カルーセルに挑戦! (2)

要はShapeなりSpriteなりにbeginBitmapFill()を使って(smoothing=trueで)描画してしまえば、画像が描画されたShapeなりSpriteは0.1px刻みで動かしてもカクカクすることはないようです。

前回のスクリプトを例に修正すると、

package  {
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	/**
	 * ...
	 * @author 393
	 */

	//ライブラリに画像を用意し、クラス名Bmpでリンケージ。
	//画像サイズは400×300

	public class Main extends Sprite{

		//private var matrix:Matrix = new Matrix();
		private var bmp:BitmapData;
		private var sp:Sprite;
		//private var mx:Number = 0;
		//private var my:Number = 0;
		//private var w:Number;
		//private var h:Number;

		public function Main() {
			bmp = new Bmp(0, 0);
			sp = new Sprite();
			addChild(sp);			
			buttonMode = true;
			this.addEventListener(Event.ENTER_FRAME , xEnterFrame);
			
			//■追加:あらかじめspにBitmapDataを描画する。
			sp.graphics.beginBitmapFill(bmp, null, false, true);
			sp.graphics.drawRect(0,0,bmp.width,bmp.height);
		}
		function xEnterFrame (e:Event):void {
			//mx += 0.05
			//my += 0.05
			//matrix.tx = mx;
			//matrix.ty = my;
			//sp.graphics.clear();
			//smoothプロパティをtrueにするのがポイント
			//sp.graphics.beginBitmapFill(bmp, matrix,false,true);
			//sp.graphics.drawRect(mx, my, 400, 300);
			//sp.graphics.endFill();
			
			//直接動かしてもカクカクしない
			sp.x += 0.05;
			sp.y += 0.05;
		}
	}
}

こちらの方が直感的に使えていいですね。

[AS][iPhone]FlashBuilder4.5のActionScriptモバイルプロジェクトで横向き固定の設定をした場合のステージサイズを正しく取得する方法

前の記事の続き、
FlashBuilder4.5のActionScriptモバイルプロジェクトでの設定のお話。

まず横向き固定にする方向は、hoge-app.xmlに

<!-- hoge-app.xml内のinitialWindow要素に指定 -->
<aspectRatio>landscape</aspectRatio> 

を設定するだけでOK。
これでデバッグプレビューすると確かに横向きでプレビューされる。
が、stage.stageWidthは320、stage.stageHeightは480と変更されていない。
(ちなみにFlexモバイルプロジェクトの場合はstage.stageWidthは480、stage.stageHeightは320となり問題ないようす)

ではどうしたらいいか、
結論としては、ドキュメントクラスのコンストラクタに以下の一行を追加
stage.setAspectRatio(StageAspectRatio.PORTRAIT);

//前提で、hoge-app.xmlに<aspectRatio>landscape</aspectRatio>と設定
stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
trace(stage.stageWidth, stage.stageHeight); //出力:320,480
stage.setAspectRatio(StageAspectRatio.PORTRAIT);
trace(stage.stageWidth, stage.stageHeight); //出力:480,320

解せないのが、なぜPORTRAITなのか。
通常はPORTRAITは縦、LANDSCAPEは横をあらわすので、stage.setAspectRatio(StageAspectRatio.LANDSCAPE);と設定すればいいと思ったのだけど、これだとstage.stageWidthは320、stage.stageHeightは480のままとなった。

//前提で、hoge-app.xmlに<aspectRatio>landscape</aspectRatio>と設定
stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
trace(stage.stageWidth, stage.stageHeight); //出力:320,480
stage.setAspectRatio(StageAspectRatio.LANDSCAPE);
trace(stage.stageWidth, stage.stageHeight); //出力:320,480

解せぬ。


※2011.8.15追記
上記設定だと実機転送した際に横方向にはならない様子。。。
やはりsetAspectRatio()にはStageAspectRatio.LANDSCAPEを設定しないと横向き固定にはなりませんでした。
上に書いたLANDSCAPE設定での懸念されるステージサイズは、
trace(stage.stageWidth, stage.stageHeight); //出力:320,480
となっているようで実機起動上では特に問題なさげ。
ステージサイズがちゃんと取れないのはデバッグプレビューだけでのバグってことなのか!?

※2011.8.18追記
コメント欄にて教えていただきました。
stage.setOrientation(StageOrientation.DEFAULT)を設定することで想定したstageサイズを取得することができました。

//前提で、hoge-app.xmlに<aspectRatio>landscape</aspectRatio>と設定
stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
trace(stage.stageWidth, stage.stageHeight); //出力:320,480
stage.setAspectRatio(StageAspectRatio.LANDSCAPE);
stage.setOrientation(StageOrientation.DEFAULT);
trace(stage.stageWidth, stage.stageHeight); //出力:480,320

[AS][iPhone]FlashBuilder4.5のActionSclriptモバイルプロジェクトにて、iOSでプレビューした際のステージサイズが正常に取れない場合

だいぶはまってネットに情報が見当たらなかったのでメモ。

FlashBuilder4.5でiPhone、Androind等のアプリを作るため、以下2つのプロジェクトが用意されました。

  1. Flexモバイルプロジェクト
  2. ActionScriptモバイルプロジェクト

1.はMXMLベースのプロジェクトで2はASだけで作れるプロジェクト。
ボクはMXMLはさっぱりなのでASだけで作れる2.を選択。
普通のFlashコンテンツを作ると同様にドキュメントクラスからゴリゴリ書いていけばOKですが、
iOS用の書き出しをした際にステージサイズの参照値おかしなことになりました。

こちらの想定は、シミュレートデバイスが、iPhone3GSなら320 × 480、
iPhone4設定なら、640 × 960となってほしいところですが、
stage.stageWidth,stage.stageHeightのtrace結果は常に、500, 375

なんぞこれ、と思えどステージサイズの設定する箇所は見当たらず。
ためしにapp.xmlのwidthとheightに直接値(320 480)を入れてみるも変化無し。
さらにドキュメントクラスにSWFのembedタグを書いてみるも変化無し。


FlashBuilderのHelpを観てみるとこんなページに行き着きました。
http://www.randytroppmann.com/2011/04/12/pure-as3-workflow-with-flex-builder-4-5/

そこに書かれていた見慣れぬ1行を追加してみると。。。

stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;

これを入れることでstage.stageWidth,stageHeightが想定通りの値となりました。
(シミュレートデバイスを変えることにより値も変化する)
とりあえず、よくわからんけど、上記一行は必須らしい。


P.S.
Retina対応もできるらしいけど、3G/3GSとRetinaの混合設定がよくわからん。。
highを書くだけで、Objective-Cみたいに自動でいい感じに変換してくれるのか、それとも2種類の画角を分けて作らないのいけないのか。。
Retinaの実機がないから検証できんのよね。。

[AS]Flash(AS3)におけるローディングバーが正常に動作しないときのひとつの理由

すげぇ、はまったのでメモしておきます。

まずは状況説明

  • テスト環境(heteml)では何の問題もなくローディングバーが表示されていた。
  • 本番環境(海外のよくわからんサーバー)にアップするとローディングバーの挙動がわけわからんようになった。
  • FFだと常に100%表示で、IEとかChromeだと常に0%表示になってしまう。

数日悩んだ後、飲み会の席で某HIGE先生に相談したら1分で解決してくれました。

「サーバーがHttpヘッダにContentLength返さない仕様だとbytesTotalがおかしくなるよ」
「ブラウザごとで解釈が違うと思うよ」


サーバー側がContentLengthを返さないのが原因かもとのこと。
対策としては、あらかじめ読み込むファイルのサイズがわかってるなら、bytesTotalを使わずにバイト数を決め打ちで入力しておく。
ファイルサイズがわからんのなら、いっそ%表示のないポンデリングローダーにしちゃう。
そんな感じでOK。


ちなみに実はAS3のリファレンスにも書いてありました。

ダウンロードデータの合計バイト数を示します。このプロパティは、ロード操作の進行中は 0 を格納し、操作が完了した時点で設定されます。また、Content-Length ヘッダーがない場合、bytesTotal の値が不確定になります。

URLLoaderのbytesTotalの項目に書かれています。
がしかし、LoaderInfoのbytesTotalの項目にはこれ書いてないんですよね。。。
今回はswfの読み込みで発生したので、LoaderInfoのbytesTotalも同様の現象が起きますよ、ということ。
なんともいやらしい罠でした。

[AS]ProgressionのPreloaderで”「Error #2044: ハンドルされていない ioError : text=Error #2036: 読み込みが未完了です。 URL: index.swf?”となってしまう場合の対処方法

ProgressionでPreloader.asからindex.swfを読み込むときにこんなエラーダイアログに悩まされてました。

どういうときにこのエラーになるかというと、Preloader.swfがindex.swfの読みこみが終わらないうちに、ブラウザを閉じてしまうとこうなります。
index.swfが重い場合、「ローディング長いからもう観なくていいや」ってブラウザ落とすと上記ダイアログで固まるんですね。
最高にうざい状態です。

で、エラーとしては、IOErrorってわかってるので、そこのエラー処理を書いてあげればいいんですけど、読み込みの処理はPreloader.asの親クラスに書いてあるので、どのオブジェクトに対して、IOErrorのハンドリングをすればいいのかわからなかったのですが、ついに重い腰をあげて親クラスの中身を覗いてみたら、5分で解決したというオチです。

結論としては、こんな感じ。

//Preloader.asの atReady()の中に一行追加
override protected function atReady():void{
//↓これを追加
this.addEventListener(IOErrorEvent.IO_ERROR, function():void { trace("エラー") } );
}

Preloader.asのatReady()の中で、thisに対してIOErrorのハンドリングを書けばOKです。

なぜ、thisに書くかといえば、Preloader.asの親クラス、CastPreloader.asの中にこの一行があるので。

_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, super.dispatchEvent );
//_loaderはindex.swfを読むためのもの

リスナー関数でsuper.dispatchEventを飛ばしてるから、Preloader.asのthisでイベントを取れるようです。

ちなみに、CastPreloader.as内の_loaderは、Preloader.asからはcontentLoaderInfoで参照が取れるので、Preloader.as内で、

//thisの代わりにcontentLoaderInfoでイベントをハンドリング
contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function():void { trace("エラー") } );

という感じで書いてみると、一応traceは出力されるんですけども、traceの前に最初のエラーが表示されてしまい、まったく意味がないことになりました。
なので、contentLoaderInfoではなく、素直にthisでIOErrorEventをハンドリングすればOK。