[AS][PHP]GMT(正確にはUTC)タイムを取得する方法

ローカルの時間(PCに設定されてる時計による)ではなく、正確な時間を取得したい場合。
ただFLASH単体では、UTC(GMTみたいなもの)の取得はできないようす。
なのでサーバーサイドのプログラムPHPを使ったら意外と簡単にできました。

↓PHPはこれだけ

<?php
//GMTを取得
$time09 = gmdate("D M d Y H:i:s", time()+9*60*60);
//ASでは、utc_timeというプロパティで扱える
echo "utc_time=".$time09
?>

FLASH側はこんな感じで、読み込みます。
以下、フレームアクション

//フレームアクション
//PHPファイルのURLを定義
var request:URLRequest = new URLRequest ( "get_utc.php" );
//送信方法をPOSTに
request.method = URLRequestMethod.POST;

//ローダーを作って、PHPを読み込み(実行)
var loader:URLLoader = new URLLoader (request);
loader.addEventListener(Event.COMPLETE, onComplete);
loader.load(request);

//PHP読み込み(実行)後の処理
function onComplete(event:Event):void {
	//↓PHPからの戻り値を変数に格納
	var vars:URLVariables = new URLVariables( event.target.data );
	//UTCデータを格納するための変数
	var utc:String;
	//データがちゃんとあった場合は、
	if (vars.utc_time!= undefined) {
		//変数に、文字列を渡す
		utc = vars.utc_time;
		next(utc);
		//ムービープレビューのときはローカルタイムを取得
	} else {
		var date:Date = new Date();
		utc = date.toString();
		next(utc);
		tf.text = "↓ローカルタイムです";
	}
}
//PHPから読み込んだ文字列を引数にしてDateインスタンスを作成で完了
function next(utc) {
	var date:Date = new Date(utc);
	tf2.text = String(date);
}

//結果表示用のテキストフィールド(2つ用意)
var tf:TextField = new TextField();
tf.autoSize ="left";
addChild(tf);
var tf2:TextField = new TextField();
tf2.autoSize ="left";
addChild(tf2);
tf2.y = 30;

ポイントはPHPから取得した文字列をそのままDateクラスのコンストラクタの引数に突っ込めば、その情報を持ったDateインスタンスが作れるところですかね。
(逆に言うと、PHPで取得する文字列は、Dateクラスで認識できる形にしてあげる必要があるってことかね)

ただこの場合の「UTCタイム」はサーバーの時計ってことになるのかな?
ってことは、サーバーの時計が狂ったらアウトってことなんだろうね。

[AS]XMLで要素数が何個あるかわからないデータを取得する時の方法 (for each ( in )の使い方) 

フレームアクションでこんな感じ。

var xml:XML = 
<sampleXML>
<test>まるいち</test>
<test>まるに</test>
<test>まるさん</test>
<test>まるよん</test>
<test>まるご</test>
</sampleXML>;

trace(xml);

var xmlArray:Array = new Array();

var sampleXMLList:XMLList = xml.test;

//配列にtest要素を突っ込む
for (var i:uint = 0; i< sampleXMLList.length(); i++) {
	xmlArray.push(xml.test[i]);
}
//配列の中身を確認
for (i = 0; i < xmlArray.length; i++) {
	trace("xmlArray その1 : "+xmlArray[i]);
}

//配列を空にする。
xmlArray = [];

//配列にtest要素を突っ込む
for each (var container:Object in xml.test) {
	xmlArray.push(container);
}
//配列の中身を確認
for (i = 0; i < xmlArray.length; i++) {
	trace("xmlArray その2 : "+xmlArray[i]);
}

プレビュー時の出力はこんな感じ

まるいち
まるに
まるさん
まるよん
まるご

xmlArray その1 : まるいち
xmlArray その1 : まるに
xmlArray その1 : まるさん
xmlArray その1 : まるよん
xmlArray その1 : まるご
xmlArray その2 : まるいち
xmlArray その2 : まるに
xmlArray その2 : まるさん
xmlArray その2 : まるよん
xmlArray その2 : まるご

注意するのは
Arrayの中身を数えるのは、Array.length
XMLListの中身を数えるのは、XMLList.length();
微妙に違うところ。

あとfor each (in)分を使えば、データの数がわからなくとも、データがあるだけループさせられる。

[AS]読み込んだXMLの内容を改行したときに余分な改行があった場合

読み込んだXMLをTextFieldに表示させるときに、
XML側では、こんな感じで書くとその中の改行がそのまま表示できる。

<?xml version="1.0" encoding="UTF-8"?>
<sample>
<description>
<![CDATA[あかさなた
はまやらわ]]>
</description>
</sample>

ただその際に改行コードの問題で、一回しか改行してないつもりでも2行で改行されてしまうときがある。

↓こうでてほしいのに
あかさなた
はまやらわ

↓こうなる
あかさなた

はまやらわ

Windowsの改行コードの問題なので、
xmlから読み込んだストリングの改行コードを変換してあげればOK

//xmlから読み込んだストリングの
var strictDescription:String = xml.description[0];
//改行コードを\nに統一する。
strictDescription = strictDescription.replace(/\r\n/g, '\n');

[AS]FMSからストリーミングした動画の表示拡大するときはVideoオブジェクトを直接拡大しないとだめっぽい。

ストリーミングした動画を拡大するときはVideoオブジェクトを直接操作したほうがいいっぽい。

たとえば、MovieClipクラスを拡張して、Videoを再生するクラスがあったとする。

package  {
	import flash.display.MovieClip;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.events.NetStatusEvent;
	import flash.net.NetStream;
	
	public class LoadMovie extends MovieClip {
		
		private var ncUrl:String
		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 ) {
			trace("FmsMain");
			this.ncUrl = ncUrl;
			this.nsUrl = nsUrl;
			
			video = new Video();
			video.width = w;
			video.height = h;
			initConnect();
		}
		
		function initConnect():void {
			
			//ネットコネクションを作る
			nc = new NetConnection();
			nc.client = new Object();
			//ネットコネクションの接続状態を調べるリスナー
			nc.addEventListener(NetStatusEvent.NET_STATUS , onNcStatus);
			//ネットコネクションを接続する。
			nc.connect(ncUrl);
		}
		//ネットコネクションの状態でいろいろ出力される
		private function onNcStatus(e:NetStatusEvent):void {
			trace("onNcStatus : "+e.info.code);
			switch (e.info.code) {
				//もし無事に接続されたら、
				case "NetConnection.Connect.Success":
				initStream();
				break;
				case "NetConnection.Connect.Failed":
				trace("onNcStatus : "+e.info.code);
				break;
				case "NetStream.Play.StreamNotFound":
				trace("not Found");
				break;
				default :
				break;
			}
		}
		
		//ネットストリームの初期化処理
		private function initStream():void {
			//ネットコネクションを作る
			var ns:NetStream = new NetStream(nc);
			//ネットコネクションの接続状態を調べるリスナー
			ns.addEventListener(NetStatusEvent.NET_STATUS, onNsStatus);
			//読み込みの時間を設定
			ns.bufferTime = 2;
			//メタデータ取得用のオブジェクトを設定
			ns.client = new Object();
			//videoインスタンスを作成
			video.attachNetStream(ns);
			addChild(video);
			ns.play(nsUrl);
		}
		
		private function onNsStatus(e:NetStatusEvent):void {
			switch (e.info.code) {
				//もし失敗したら
				case "NetStream.Play.StreamNotFound":
				trace("NetStream Not Found");
				break;
				
				default :
				trace("onNsStatus : "+e.info.code);
			}
		}
		
	}
	
}

このクラスを利用するときはフレームアクションで、
(ローカルにelevator.flvがあるのが前提)

var ncUrl:String = null
var nsUrl:String = "elevator.flv"
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl);
addChild(fms2);

この動画のサイズを変えたいときに

var ncUrl:String = null
var nsUrl:String = "elevator.flv"
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl);
addChild(fms2);
fms2.width = 500;
fms2.height = 500;

まあlこれでも拡大されるのは予想通り。
ただしFMSのflvを読んでる場合は拡大されるずに動画が非表示になる。
(音はなってるから再生はされてるようす)

//これだと動画がなぜか表示されない
var ncUrl:String = "rtmp://hoge.moge"
var nsUrl:String = "doke"
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl);
addChild(fms2);
fms2.width = 500;
fms2.height = 500;

LoadMovieクラスには、引数w,hでvideoオブジェクトのwidth、heightを操作できるようにしてあるので、

//これなら動画の大きさを操作できる。
var ncUrl:String = "rtmp://hoge.moge"
var nsUrl:String = "doke"
w = 500;
h = 500;
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl,w,h);
addChild(fms2);

もちろんローカルのflvを再生するときもこのVideoオブジェクトを操作する方法でOK。
なので、つねにこっちの方法を使った方が安全っぽい。


上記の例でFMSから読み込んだ場合のLoadMovieオブジェクトのwidthとheightは、w,hをどんな値を設定しても0になるようだ。
(ローカルflvを読み込んだ場合は指定した数値となる)

//FMSの場合
var ncUrl:String = "rtmp://hoge.moge"
var nsUrl:String = "doke"
w = 500;
h = 500;
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl,w,h);
addChild(fms2);
trace(fms2.width) //出力:0
trace(fms2.height) //出力:0

ローカルflvを読み込んだ場合

//ローカルFLVの場合
var ncUrl:String = null
var nsUrl:String = "elevator.flv"
w = 500;
h = 500;
var fms2:LoadMovie = new LoadMovie(ncUrl,nsUrl,w,h);
addChild(fms2);
trace(fms2.width) //出力:500
trace(fms2.height) //出力:500

FMSから読み込んだものは実体がないということなのかな?

[AS]FMSを使った動画再生をするときの設定の注意点

FMS:Flash Media Server
ローカルのflvのprogressive再生のときとFMSでの設定の違いをメモしとく
NetConnectionの設定で
ローカルflvの時は

//ローカルのflvを再生するとき
var nc:NetConnection = new NetConnection();
nc.connect(null);

FMSのときは、

//FMSを使うとき
var nc:NetConnection = new NetConnection();
nc.connect("rtmp://hoge.moge.ore.dore");
nc.client = new Object();

FMSのときはnc.connectにURLを設定(flvファイルの手前のフォルダまで)
※上の例はURLが rtmp://hoge.moge.dore.flvだった場合
あと、nc.client、ローカルflvでは設定しなくていいけど、FMSのときは設定してあげないとエラーになる。

あとNetStreamの設定
ローカルflvのときは、

var ns:NetStream = new NetStream(nc);
ns.play("doke.flv")

FMSのときは

var ns:NetStream = new NetStream(nc);
ns.play("doke")

FMSのときは、.flvをつけると宜しくないようす。

情報追記:2011/02/22
http://memo.393.bz/archives/1480

[AS]外部swfをLoaderで読み込んだ後にunload()で消すときは子swfの中身も綺麗に消す

外部SWFを読み込んで、それを破棄するときはunloadして、removeChildしてnullにするのが良いらしい。
親swfにはこんな感じでかく。

loader.unload()
removeChild(loader);
loader = null();

親swfで子swfをunload()するとに子swfはaddEventListenerでイベントを受けとれる。
子swfでこんな感じ

this.loaderInfo.addEventListener(Event.UNLOAD , unloadHandler);

そこで子swfで設定した参照なり、イベントは破棄してあげないといけない。

例)
ステージにm0_mc、m1_mcというインスタンス名のMCを二個配置。
m0_mcクリックでm子swfの読み込み、m1_mcクリックで子swfを破棄
親swfのドキュメントクラス

package {
	//親swfのドキュメントクラス
		import flash.display.Loader;
		import flash.net.URLRequest;
		import flash.display.LoaderInfo;
		import flash.events.Event;
		import flash.events.MouseEvent;
		import flash.display.MovieClip;
		
		
	public class MainP extends MovieClip {

		var loader:Loader

		public function MainP() {
			m0_mc.addEventListener(MouseEvent.CLICK , movieOn);
			m1_mc.addEventListener(MouseEvent.CLICK , movieOut);
		}
		private function movieOn(e:MouseEvent):void {
			trace("movieOn");
			//子SWFの読み込み
			loader= new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,xComplete);
			var urlReq:URLRequest = new URLRequest("child.swf");
			loader.load(urlReq);
		}
		private function movieOut(e:MouseEvent):void {
			loader.unload();
			removeChild(loader);
			loader = null;
		}
		
		public function xComplete(e:Event):void {
			addChild(loader);
		}
	}
}

子swfのドキュメントクラス

package  {
	//子swfのドキュメントクラス
	import flash.display.MovieClip;
	import flash.events.Event;
	
	public class Child extends MovieClip {
		
		public function Child() {
		trace("child");
		this.addEventListener(Event.ADDED_TO_STAGE, addc);
		this.addEventListener(Event.REMOVED_FROM_STAGE, removec);
		this.loaderInfo.addEventListener(Event.UNLOAD, unloadc);
		}
		function addc (e:Event):void {
			trace("child add");
		}
		function removec (e:Event):void {
			trace("child remove");
		}
		function unloadc (e:Event):void {
			trace("child unload");
		}
	}
}

[AS] 外部SWFファイルの読み込みで、子swf側で動的に配置したmcが表示されない。

すげー悩んだのでメモ。
親SWF(parent.swf)で、ドキュメントクラス Main.asを設定して、外部swfを読む処理はこんな感じ

//ドキュメントクラス
package {
		import flash.display.Loader;
		import flash.net.URLRequest;
		import flash.display.LoaderInfo;
		import flash.events.Event;
		import flash.display.MovieClip;
		
	//親ファイルのドキュメントクラス
	public class Main extends MovieClip {
		var loader:Loader
		private var loaderRoot:Object;

		public function Main() {
			loader= new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,xComplete);
			var urlReq:URLRequest = new URLRequest("test.swf");
			loader.load(urlReq);
		}
		public function xComplete(e:Event):void {
			addChild(loader);
		}
	}
}

子swf(test.swf)はこんな感じで、動的に何かを表示させてみる。
(ステージには他にシェイプとか文字とかおいておく。)

package  {
//ドキュメントクラス
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class Main extends Sprite {
		public function Main() {
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
		}
	}
}

一見平気そうにみえるけど、これでparent.swfを開くとtest.swfで定義した四角が表示されてない・・・
test.swf単体で開けば四角は表示されるのに、parent.swfを開くと表示されない。
Flash上で、ステージにおいたものは表示されているので、swfを読み込めてないわけではない。

なんだこりゃー!!!!!と思ったら・・・

読み込み側のswfのドキュメントクラス名と、読み込まれる側のドキュメントクラス名が同じだとまずいようす。
(今回は両方とも、Main.asとなっている)
というわけで、どちらかを変えてあげればOK。
子swf側を変えるのであればこんな感じ。

package  {
//MainからTestMainに変更
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class TestMain extends Sprite {
		public function TestMain() {
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
		}
	}
}

もちろんflaファイルの設定も変えなきゃね。

ちなみに、ドキュメントクラス名が一緒の状態で、子swf側でステージ上のインスタンス名を取得しようと以下のようなエラーがでました。

//ステージに_mcというインスタンス名のMCがある状態で。
package  {
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class Main extends Sprite {
		public function Main() {
			
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
             //これはエラーになる。
			var mc:MovieClip = MovieClip(this.getChildByName("_mc"));
		}
	}
}

ReferenceError: Error #1056: Main のプロパティ _mc を作成できません。
at flash.display::Sprite/constructChildren()
at flash.display::Sprite()
at flash.display::MovieClip()
at Main()

[AS]progression DoTweenerをinterrupt()するのは大変かも。

SerialListとかのコマンドリスト内にもかけて、とっても便利なDoTweener。
ただし、そのコマンドリストを停止させるときはちょっと注意が必要かも。
具体的には、DoTweenerの含まれているコマンドリストをinterrupt()するとDoTweenerでエラーが吐かれます。
一応、その場で止まってるみたいですが、内部を読めない素人にはエラーという言葉が怖い・・・

というわけで・・・
interrupt()するコマンドにはTweenerを利用するという方法に逃げました。
これだとinterrupt()してもとりあえずエラーは表示されません。
(もちろん内部的に何かエラーが発生してるかはわからんですが・・・)

[AS]progression Sceneの_onLoad中にコマンドをinterrupt()すると良くない?

ちょっとはまったのでメモ

progressionのコマンド機能を使ってオープニングムービー付きサイトを作りました。
で、当然ムービーはスキップできるようにしたいな、と。

コマンドを停止させるにはinterupt()という命令でOK。
ただ停止したいコマンドを指定せねばなので、_onLoad()内のaddCommand内に書いてたコマンドを、別でSerialListを作ってそこにaddCommand、それを_onLoad()のaddCommandにinsertCommand()する感じにしました。
↓だいたいこんなかんじ
(indexSceneに書いたってことで話を進めます)

protected override function _onLoad():void {
	var sList:SerialList = new SerialList();
	sList.addCommand(
		new Trace("insertCommand sList")
	)
	addCommand(
		insertCommand(sList)
	);
}

で、skipボタン用意して押したら、sList.interrupt()を実行して、その後はスキップ後の処理を実行するような感じに。

結果としては、止まった。
でも、通常_onLoad()後に行われる_onInit()は実行されず。
強制ストップだからそういうもんなのかな。
で、ここで問題が。
indexSceneから移動しても、_onGotoイベント、_onUnLoadイベントが発行されないご様子。
_onInit()が実行されてないとだめなのか?と思ったので・・・
スキップしたいコマンドをonInit内に記述して、onInit()のコマンドをinterruptしてみると、

//IndexSceneoの_onInitに書いてます。
protected override function _onInit():void {
	var sList:SerialList = new SerialList();
	sList.addCommand(
		new Trace("insertCommand sList")
	)
	addCommand(
		insertCommand(sList)
	);
}

こんどはシーン移動の際に_onGotoイベントが問題なく発行されました。

_onLoad中でのコマンドのinterrupt()には注意が必要かもね。

[AS]progression Progressionインスタンスの取得方法

下の記事でCastSpriteからprogressionインスタンスを取得するのに、indexSceneを取得して、そのprogressionプロパティから取得しましたが、もっと簡単にできました。

trace("プログレッションid" + getProgressionById("index"))

“index”というidは、index.asのここで定義されてます。

// Progression インスタンスを作成します。
prog = new Progression( "index", stage, IndexScene );