「Flash」カテゴリーアーカイブ

TweenerのuseFrames


Tweenerを普段良く使うのですが、あまり使ってなかった機能で、最近便利だと思った機能があります。useFramesというオプションです。

使い方は次のような感じ。

Tweener.addTween(target, {useFrames:true, alpha:1, time:3, transition:'easeOutQuad'});

useFramesをtrueにすると、timeの値がフレーム数で指定できるようになります(この場合は3フレーム)。また、アニメーションもタイマーではなく、フレームレートによるタイミングで動作します。(古いリビジョンでは動かないとありますが、最新のものは大丈夫そうです)

つまりフレームレートが落ちているときに、タイミングがずれたりしないのです。これがすごく大事。

タイムラインアニメーションとスクリプトによるアニメーションを併用することがわりとあると思いますが、Tweenerは基本的にタイマーで動作するため、フレームレートが落ちたときに、アニメーションがずれてしまうことがあります。このオプションでこのズレを回避できます。

できるだけフレームレートが落ちるような処理を回避して軽く作る、というのが大事と思っていますが、低スペックマシンで再生したときに変な動きをする、あるいはエラーで止まってしまうなんて事が無いように、両方使う場合には使ってみるといいかもしれません。

他のTweenライブラリでこういうフレームレートに応じた処理を入れてるライブラリはあるんだろうか・・・と思ったらBetweenAS3にもあるみたい(_frame)。さすが。

Eventメタデータタグを便利に使う


AS3のEventメタデータタグは何のためにあるの?というTogetterを見て、そういえば自分も調べたけど何も書いてなかったなぁと思ったのでメモ。

自分でカスタムイベントを作成すると、addEventListenerしたときのコードヒントにカスタムイベントが出てこなくて面倒だな、と思っていたんですが、Eventメタデータタグを使うと楽ができます。

ActionScriptのメタデータタグについては公式のこちらをどうぞ。

使い方は

[Event(name="eventName", type="package.eventType")]

というような感じで、カスタムイベントクラスの中・・・ではなく、そのイベントをdispatchするクラス(IEventDispacherなクラス)に書く感じになります。

すると、他でそのdispatchするクラスを使ってaddEventListenerしたときにコードヒントがでてくれます。

注意点としては、メタデータタグを書く位置をclassの書いてある位置の上(public class Hoge{}より外側)にすること。メタデータタグ内のプロパティは2重引用符/ダブルクォーテーション(“)で囲うこと

普段文字列はシングルクォーテーション派なので、何をどうやっても動かず、かなり悩まされました。。普通ダブルクォーテーションなんですかね。(FlashBuilderでの場合。FlashDevelop他では動くんでしょうか)

また、Eventのtypeに入れた名前がそのまま定数として大文字+アンダーバーに変換されるので、「hogeFugaXML」みたいなtypeにすると、「HOGE_FUGA_X_M_L」とか残念な変換をされてしまいます。これが実際のEvent側で用意している定数と違う場合、動作しなくなってしまうので注意が必要そうです(この場合はhogeFugaXmlと書けば正常に動作するはず)。

自分で作ったクラスの場合は特に書かなくてもいい場合がほとんどと思いますが、人に渡すものだったりする場合は、書いたほうが親切かなと思うので、有効に使っていきたいです。

参考:

カスタムクラスのプロパティをfor..inで出力


wonderflに昔、こんな感じでforkしてみたものがあったんですが、理由が分からなかった部分を調べたまとめ。

通常、for..inをした場合、Objectとして作成したものは出力できますが、カスタムクラスのプロパティだとそれができません。

//Objectだとできる
for(var i:uint = 0; i < LOOP; i++){
    var id:String = 'id' + String(i+1);
    var name:String = 'name' + String(i+1);
    var obj:Object = new Object();
    obj.id = id;
    obj.name = name;
    _listObject.push(obj);
}
for(i = 0; i < LOOP; i++){
    var o:Object = _listObject[i];
    for(var prop:String in o){
        trace(o[prop]);//できる
    }
}

//カスタムクラスのプロパティはでない
for(var j:uint = 0; j < LOOP; j++){
    var id:String = 'id' + String(j+1);
    var name:String = 'name' + String(j+1);
    var obj:TestObject = new TestObject();
    //TestObjectはidとnameをpublic変数に持つものとする
    obj.id = id;
    obj.name = name;
    _listObject.push(obj);
}
for(j = 0; j < LOOP; j++){
    var o:Object = _listObject[j];
    for(var prop:String in o){
        trace(o[prop]);//何も出力されない
    }
}

何故できないのか、と考えてみたんですが、for..inする場合には、そのクラスでpropertyIsEnumerable(プロパティ名)というメソッドを使った時にtrueが帰ってくる必要がありそうです。

ただ、こちらのメソッド、Objectに定義されているものをoverrideすることはできないので、書く必要があるんですが、全てtrueが帰るようにしてもうまく動作しません。prototypeを拡張しても効果無しです。

しかし、たまたまいじっていたらpropertyIsEnumerableがtrueになった、というかfor..inで出力されるようになりました。そのクラスのprototypeにプロパティと同名の変数を定義した場合です。

Object型の場合は値を代入した瞬間に同様の処理をされているのか?とも思いますが、理由は良く分かりません。

サンプルはこちら。

どうせデバッグ用にしか使わないだろうので、処理速度がどう変わるのか、などは検証していません。これが自動化できれば凄く便利な気もします。ただそもそもプロパティの列挙をするためにプロパティを全て知らなければいけないという面倒さがありますし、整形して出力したければ、toStringを実装する時にそのように書いた方が楽な気がしますし。

というわけで、調べたことのメモでした。

マスクしているオブジェクトを変えるとマスクされているオブジェクトがおかしくなる不思議


再現方法

  1. タイムラインのレイヤー1にマスクしたいオブジェクトを配置する(maskedObjとする)
  2. タイムラインのレイヤー2にマスクするオブジェクトを配置する(maskObj1とする)
  3. レイヤー2をレイヤー1のマスクに設定する
  4. レイヤー2の適当なフレームに空のキーフレームをうち、別のマスクするオブジェクトを配置する(maskObj2とする)
  5. レイヤー1をレイヤー2のフレーム数まで伸ばす

この状態で、maskedObjに対して、maskObj1にマスクされている時に操作した情報は、マスクがmaskObj2に変わった瞬間に消えるみたいです。普通はremoveしないと消えないようなaddChildしたオブジェクトや、addEventListenerしたイベントがまるっと消えてしまいます。

マスクしているオブジェクトが変わっただけなのに、マスクされているオブジェクトが初期化されてしまうのです。明らかに変です。

FlashCS4でもFlashCS5でもなります。理由は謎です。ちなみにActionScriptから操作した場合は問題ないようです。Flashのタイムラインからマスクをかけた場合は、複数オブジェクトに対してマスクがかけられたりもするので、動作そのものが違うためでしょうか。

「マスクはASで操作する」あるいは「後で付加するあるいは後で付け直す」とすればこのバグを防げるみたいです。

テスト用fla+asファイル

有名なバグだったりするんでしょうか?こんなので何時間も取られてはたまらないですよね。・・・それは金曜日の私ですw

カスタムクラスのインスタンスをtraceした時に配列のように内容を出力したい


久々に。仕事がてんてこまいだったのがようやく落ち着いてきました。

表題の件、ずっとできるはず、と思いながらうまく行かなかったのですが、ある時ふと思い立って調べ直してみたらうまく行ったのでメモ。

SpriteやMovieClipを拡張して作るときは、toString()をoverrideして、戻り値を書き出したい値にすればできる様子。

ただ、Objectを拡張しただけのクラス(何もextendsしてないクラス)だとこれだとできない。

Error: Method marked override must override another method.

みたいに怒られます。

どうしたらいいものかと思ってたんですが、直に

public class Hoge {
    public var name:String;
    public var text:String;
    public function toString():String {
        return '['+[name, text]+']';
    }
}

と書けばちゃんとtraceした時にtoStringとして動作するようです。いろいろ調べてみたらtoStringはObjectのObject.prototype.toStringとして書かれているというような記述がありました。

参考:Adobe ActionScript 3.0 * 高度なトピック

真似してみたらこれでもできた。

public class Hoge {
    public var name:String;
    public var text:String;
    prototype.toString = function():String {
        return '['+[this.name, this.text]+']';
    }
}
//----------
var hoge:Hoge = new Hoge();
hoge.name = 'nameTest';
hoge.text = 'textTest';
trace(hoge);  //[nameTest,textTest]

prototypeオブジェクトについてもうちょっと調べてみようと思います。