XMLの名前空間を指定解除する場合の注意点

AS3.0では名前空間を持つXMLは、名前空間を使わないとアクセスできない。で、名前空間を指定した後クリアしていたんだけど、そのクリア方法に問題在り。nullでなくundefinedを使うこと。
nullだと次にXMLを使ったときに、namespaceの初期値が xmlns=”null” になってしまう。わからなくてさんざん悩んだ。ざっくりだけど、該当部分だけ貼っておきます。一番最後が問題だった箇所です。

//名前空間を抽出・保存(接頭辞で指定)
for (var i:int = 0; i<doc_xml.namespaceDeclarations().length; i++) {
switch (doc_xml.namespaceDeclarations()[i].prefix) {
case “” :
var ns_def:Namespace = new Namespace(doc_xml.namespace(doc_xml.namespaceDeclarations()[i].prefix));
break;
case “dc” :
var dc:Namespace = new Namespace(doc_xml.namespace(doc_xml.namespaceDeclarations()[i].prefix));
break;
case “rdf” :
var rdf:Namespace = new Namespace(doc_xml.namespace(doc_xml.namespaceDeclarations()[i].prefix));
break;
}
}

//名前空間のデフォルト値を設定

default xml namespace = ns_def;

//(XMLの処理)

//名前空間のデフォルト値を設定解除(ここはundefinedで。nullは不可)
default xml namespace = undefined;

テキストノードがない場合の戻り値

xmlで引っかかった点。

テキストノードを表示する処理を書いていて、字数制限が必要でString処理を挟んだら動かなくなった。テキストノードに値がないので、空白かな〜と思っても判定できず。調べてみたら、テキストノードに値がない場合の戻り値はundefinedだった。
こんな感じでテスト。

var test_xml:XML =
<itemlist>
<msg>おはよう</msg>
<msg></msg>
<msg>こんにちは</msg>
<msg />
<msg>おやすみ</msg>
</itemlist>;

for each(var item:XML in test_xml.msg) {
trace(“text=”+item.text(),(item.text() == “”),(item.text() == null),(item.text() == undefined))
}

——出力結果

text=おはよう false false false
text= false false true
text=こんにちは false false false
text= false false true
text=おやすみ false false false

BitmapData.draw()とクロスドメイン

画像をロードする処理で、同じ画像を何度も読み込むのはムダなのでビットマップをコピーして使うことにしたら、BitmapData.draw()で止まってしまった。いろいろ調べてみたら・・・

・別ドメインの画像を読み込むだけなら、ポリシーファイルがなくても問題なし。
・別ドメインの画像を読み込んでデータの操作をする場合は、ポリシーファイルが必要。

ということらしい。
BitmapData.draw()のヘルプには、次のように書いてる。

読み込んだビットマップイメージを source として使用する場合も制限があります。読み込んだイメージが呼び出し元と同じドメインからのものの場合、draw() メソッドの呼び出しは成功します。また、イメージのサーバーのクロスドメインポリシーファイルで、draw() メソッドを呼び出す SWF コンテンツのドメインに許可を付与できます。この場合、LoaderContext オブジェクトの checkPolicyFile プロパティを設定し、このオブジェクトを context パラメータとして、イメージの読み込みに使用する Loader オブジェクトの load() メソッドを呼び出すときに使用する必要があります。これらの制限は、アプリケーションセキュリティサンドボックス内の AIR コンテンツには適用されません。

う〜ん、知らなかった。毎回のload()で設定するのは面倒なので、Security.loadPolicyFile()でポリシーファイルを指定することで解決。
下記のサイトにお世話になりました。

動的生成したオブジェクトはインスタンス名で参照できない

例えば、次のようにMovieClipを動的に階層化する。

var target_mc:MovieClip = new MovieClip();
this.addChild(target_mc)

var img_mc_on:MovieClip = new MovieClip();
var img_mc_off:MovieClip = new MovieClip();

target_mc.addChild(img_mc_on);
target_mc.addChild(img_mc_off);

これを

trace(“target_mc.img_mc_off”,target_mc.img_mc_off,target_mc.img_mc_off.name)

とすると、下記のようにエラーになる。

TypeError: Error #1010: 条件は未定義であり、プロパティがありません。

「ああ、インスタンス名をつけておかないといけないのか」と、下記のように変更しても同じエラーが出る。

var target_mc:MovieClip = new MovieClip();
this.addChild(target_mc)

var img_mc_on:MovieClip = new MovieClip();
var img_mc_off:MovieClip = new MovieClip();

img_mc_on.name = “img_mc_on”;
img_mc_off.name = “img_mc_off”;

target_mc.addChild(img_mc_on);
target_mc.addChild(img_mc_off);

trace(“target_mc.img_mc_off”,target_mc.img_mc_off,target_mc.img_mc_off.name)

「何で???」と悩んでいたら、下記のサイトを発見。
要は「動的に生成したインスタンスは、インスタンス名で参照できないので、addChildしたオブジェクトのプロパティにインスタンス本体を入れておく」必要があるらしい。で、次のように改訂。

var target_mc:MovieClip = new MovieClip();
this.addChild(target_mc)

var img_mc_on:MovieClip = new MovieClip();
var img_mc_off:MovieClip = new MovieClip();

img_mc_on.name = “img_mc_on”;
img_mc_off.name = “img_mc_off”;

target_mc.addChild(img_mc_on);
target_mc.addChild(img_mc_off);

//これが重要!!!
//動的に生成したインスタンスは、インスタンス名で参照できないので
//addChildしたオブジェクトのプロパティにインスタンス本体を入れておく。
target_mc[“img_mc_on”] = img_mc_on;
target_mc[“img_mc_off”] = img_mc_off;

trace(“target_mc.img_mc_off”,target_mc.img_mc_off,target_mc.img_mc_off.name)

これで下記のように正しく参照されてることが確認できます。

target_mc.img_mc_off [object MovieClip] img_mc_off

ちなみにnameを設定しない場合には、下記のようになります。

target_mc.img_mc_off [object MovieClip] instance3

オーサリングでステージ上に作成したオブジェクトは、AS2.0までと同じく使えるのでわかりにくいですね。
上記F-siteの記事によれば『前述「オーサリング時に配置したMovieClipインスタンス』では、タイムラインに予め配置して[プロパティ]インスペクタで設定したインスタンス名は、そのまま変数のように参照として扱うことができました。これは、ActionScript 3.0では、Flashがインスタンス名と同じ名前の変数を自動的に宣言して、そこにインスタンスの参照を格納する仕組みになっているからです」ということらしい。う〜ん。こういうことは実際に遭遇してみないと気づかないですね。
でも最初に作ったtarget_mcが問題なく参照されているのは何でだろう?推察するに、ステージ(タイムライン)上に生成したオブジェクトは、オーサリング時に配置したオブジェクトと同様に自動的に処理されるってことなんでしょうね。
<追記>2010.8.18
最後の「でも最初に作ったtarget_mcが問題なく参照されているのは何でだろう?」については、野中さんから「参照には変数に設定されている必要がある」というご指摘いただきました。詳しくはコメント欄を参照のこと。

swf全体でのサウンド制御

AS3.0ではサウンド制御も変更されていて、基本的にSoundクラスとSoundChannelクラスを使う。例えば、外部mp3を再生する場合は。こんな感じ。

var sd_song:Sound;
var sd_channel:SoundChannel;

function xInitSound_song():void {
sd_song = new Sound();
var url:String = “mp3/” + “Pissism-Blade.mp3”;
var urlReq:URLRequest = new URLRequest(url);

sd_song.addEventListener(Event.COMPLETE,onCompleteSound_load);
sd_song.load(urlReq);
}

function onCompleteSound_load(evt:Event):void {
trace(“—ロード完了”,evt.target);
}

function xStartSound_song(evt:MouseEvent):void {
sd_channel = sd_song.play();
}

xInitSound_song();
btn_play.addEventListener(MouseEvent.CLICK,xStartSound_song);

で、使用しているチャンネルの音量は、SoundChannel.soundTransform.volumeで制御することになるのだけれど、

function xVolume0_song(evt:MouseEvent):void {
var st:SoundTransform = sd_channel.soundTransform
st.volume = 0
sd_channel.soundTransform = st
}

と書いても、サウンド再生中は制御できる(volume=0になる)けど、停止しているときに0にしても再び再生するとvolumeは0にはならない。要はSoundChannelのsoundTransformは再生しているSoundクラスに対してのみ有効ということらしい。SoundChannelという器の設定ではないんですね。
コレだと全体の音量制御ができない。いろいろ検索してみたら、SoundMixer.soundTransformで制御すると、swf全体の制御が出来るらしい。

function xVolume0_song(evt:MouseEvent):void {
var st:SoundTransform = SoundMixer.soundTransform;
st.volume = 0;
SoundMixer.soundTransform = st;
}

SoundMixerクラスを使うことで、swf全体のサウンド制御ができ、かつサウンドの再生・停止に依存せずに音量制御ができる。
検索中に下記サイトでこの辺の制御をクラス化したものを発見。コレ、便利そう。