Home > Flash/Flex

Web 酒 肴

«前へ || 1 | 2 | 3 | 4 | 5 | 6 || 次へ»

[Flex]カスタムエフェクトの作成 #6 - MaskEffectの継承2

先日の「お花見勉強会 in 大阪」で軽く紹介しました。 桜の画像がどうにもキモくてお気に入りです。

やってることは前回までのエントリーに加えて桜を斜め上から順番に表示しているだけ。 内部的にはかなりムチャやってて、実用的にしようと思ったらあと3倍くらいの時間がかかるでしょう。

ちゃんと本当に使えるライブラリに仕上げて5月30日の京都でのFlex勉強会で報告するつもりです。

[Flex]カスタムエフェクトの作成 #5 - MaskEffectの継承1

Adobeのドキュメントで MaskEffect の仕様を調べる。

MaskEffect - ActionScript 3.0 言語およびコンポーネントリファレンス

どうやらターゲットコンポーネントに対して、文字通りマスクとなる矩形を表示して部分的に隠し、そのマスクを時間の経過とともに移動したり拡大/縮小したりして様々なエフェクトを実現する、というものだった。

後日、いろいろ格闘して↑は少し違うことに気づいた。「矩形で隠す」のではなく、「矩形以外を隠す」なのだ。 つまり、のぞき穴からのぞき込んでいる状態だ。

実装するのは大きくわけると以下の3つになるようだ。

  1. マスクとして使用される Sape オブジェクトを作成するメソッド
  2. マスクの拡大/縮小率の From, To の指定
  3. マスクの移動元/移動先の指定

読んでみたがよく分からない。 こういうときは似たサンプルを探す。

ってことでほぼ同じ処理(というかもっと高級な処理)を実現している mx.effects.WipeDown クラスのソースコードを見て作った。


MaskSample1Effect

package
{
    import mx.effects.IEffectInstance;
    import mx.effects.MaskEffect;

    public class MaskSample1Effect extends MaskEffect
    {

        public function MaskSample1Effect(target:Object=null)
        {
            super(target);
            instanceClass = MaskSample1EffectInstance;
        }

        override protected function initInstance(inst:IEffectInstance):void{
            super.initInstance(inst);

            MaskSample1EffectInstance(inst).createMaskFunction = MaskSample1EffectInstance(inst).createMask;
        }
    }
}

MaskSample1EffectInstance

package
{

    import flash.display.DisplayObject;
    import flash.display.Graphics;
    import flash.display.Shape;
    import flash.geom.Rectangle;

    import mx.core.FlexShape;
    import mx.effects.effectClasses.MaskEffectInstance;

    public class MaskSample1EffectInstance extends MaskEffectInstance
    {
        private var sx:Number;
        private var sy:Number;

        public function MaskSample1EffectInstance(target:Object)
        {
            super(target);
        }

        override protected function initMaskEffect():void{

            super.initMaskEffect();

            xFrom = this.sx;
            yFrom = this.sy;
            xTo = -this.sx;
            yTo = -this.sy;
        }

        public function createMask(targ:Object, bounds:Rectangle):Shape{
            var newMask:Shape = new FlexShape();
            var angle:Number = Math.atan2(bounds.height, bounds.width);
            var cos:Number = Math.cos(angle);
            var sin:Number = Math.sin(angle);
            var targetWidth:Number = bounds.width / cos;
            var targetHeight:Number = cos * bounds.height + sin * bounds.width;

            var g:Graphics = newMask.graphics;
            g.beginFill(0xFFFF00);
            g.drawRect(0, 0, targetWidth, targetHeight);
            g.endFill();
            newMask.rotation = angle * 180 / Math.PI;

            sx = sin * sin * bounds.width;
            sy = - sin * cos * bounds.width;

            return newMask;
        }

        override public function finishEffect():void{
            super.finishEffect();

            DisplayObject(target).visible = false;
        }
    }
}

ほとんど WipeDown クラスからコード持ってきて、余計な部分や今の理解に必要のない部分を消去した。 そして、矩形を斜めにして右上から消えていくようにした。

MaskSample1Effect クラスは何もしていない。作っただけ。

MaskSample1EffectInstance クラスでは以下の2つのことをしている。

  1. initMaskEffect メソッドでマスクの移動元と移動先の (x, y) 座標をセットする。
  2. finishEffect メソッドでエフェクト終了後にオブジェクトを見えなくする。

ポイントは以下の部分だけ。

xFrom = (マスク移動元のx座標)
yFrom = (マスク移動元のy座標)
xTo = (マスク移動先のx座標)
yTo = (マスク移動先のy座標)

マスクの移動元と、移動先の (x, y) 座標を指定するだけで勝手に動いてくれる。 その他はマスクを斜めに向けたり、斜めのマスクが全体をスッポリと覆えるように大きさを調整したりしただけ。

できた。

文字の消え方がおかしいことを除けば今はこれくらいでいい。 厳密には消える対象のオブジェクトが回転とか拡大・縮小されてもちゃんと動作するようにしなければならない。 (WipeDown はそうなってた。) けど、今はそこまでやる段階ではないからいいっしょ。

[Flex]カスタムエフェクトの作成 #4 - さらに複雑なアニメーションへ

前回までのは単なるサンプルに少し手を加えただけのもの。 あんなのでは桜を表現したエフェクトなどできるはずもない。 ここからはより実践的に調査を続けよう。

まず、要求定義。

最終目標

  • 桜の花びらが舞いながらオブジェクトがフワーッと消えていくようなエフェクト

うん、めっちゃ難しそう\(^o^)/

もう少しハードルを下げる。

第2目標

  • オブジェクトが端から徐々に消えていく。消えていくときの境界線に桜の花びらが表示される

このくらいか。 この要件を満たすために必要な要素を、自分の実現可能なパーツに分解しよう。 これは何もソフトウェア開発に限らず、あらゆる問題解決に有効なアプローチだ。

  1. オブジェクトを端から徐々に消す
  2. 桜の花びらを一か所に表示する
  3. 画像を線上に均等に並べて表示する(2と組み合わせれば桜を線状に並べて表示できる)

細かいところをあげたらもう少し色々ありそうだけど、このくらいでよし。

さて、2はたぶん問題無い。 画像に(x, y)座標を指定して addChild してやればいいのだろう。

3は数学的アプローチ。 直線状の座標を均等に分割する点を計算してやれば2と組み合わせればできるだろう。 中学で習った1次関数の応用、もしくは高校で習うベクトルの問題に落としこめるので、たぶんできる。

問題は1だ。 これは今の自分ではできない。 これについて調査すればすべての問題が解決できるはず。

実はこれについては似たようなものを見たことがある。 いや、というか見たことがあるから、これならできると思って要求定義をこのようにしたと言ってもいい。 mx.effects.WipeDown というエフェクトクラスがあるのだ。 これはオブジェクトを上から下へスーッと描画する。

消していくのと描画するのとでは、動作としては逆だが部分的に表示し、部分的に隠すという意味ではどちらも同じだ。 このWipeDownについて調査することから始めた。

調査となれば、まずはAdobeのドキュメント。 Adobeは相当Flex/Flashに期待をかけているのか非常にドキュメントが充実している。日本語への直訳がわかりにくい、という欠点はあるけど、結果的にAdobeのドキュメントを調べることで問題解決することが経験的にも多い。

WipeDown - ActionScript 3.0 言語およびコンポーネントリファレンス

さて、ここで mx.effects.WipeDown のリファレンスを見て気づいたこと。それは

  1. mx.effects.MaskEffect を継承している
  2. mx.effects.WipeDown が独自に設定しているプロパティ、メソッドは無く全て MaskEffect からの継承

どうやら MaskEffect がオブジェクトの一部を Mask (隠す)するのに必要なインターフェースを提供しているようだ。 じゃあこいつを使えば一発解決かも?

ということで次にすべきことは MaskEffect を継承したエフェクトを作成すること。

進め、どんどん。

[Flex]カスタムエフェクトの作成 #3 - TweenEffectによる複数パラメータの変化

前回はターゲットオブジェクトに対してrotationプロパティだけを変化させてアニメーションを実行した。 では、複数のパラメータを同時に変化させるにはどうしたらいいのだろう?ということで調べてみた。

答えは簡単。

インスタンスクラスで createTween メソッドを実行するときに開始値, 終了値を指定するところに配列を指定してやればいい。

createTween(this, [開始値1, 開始値2, ...], [終了値1, 終了値2, ...]);

前回のサンプルでrotation と同時に scaleX , scaleY も変化するアニメーションに修正してみた。

TestEffect.as

package
{
    import mx.effects.Effect;
    import mx.effects.IEffectInstance;

    public class TestEffect extends Effect
    {
        public function TestEffect(target:Object=null)
        {
            super(target);
            instanceClass= TestEffectInstance;
        }

        override public function getAffectedProperties():Array{
            return ["rotation"];
        }

        override protected function initInstance(inst:IEffectInstance):void{
            super.initInstance(inst);
            TestEffectInstance(inst).angleFrom = 0;
            TestEffectInstance(inst).angleTo = 360;
            TestEffectInstance(inst).scaleFrom = 2;
            TestEffectInstance(inst).scaleTo = 1;
        }
    }
}

TestEffectInstance.as

package
{
    import mx.effects.effectClasses.TweenEffectInstance;

    public class TestEffectInstance extends TweenEffectInstance
    {
        public var angleFrom:Number;
        public var angleTo:Number;
        public var scaleFrom:Number;
        public var scaleTo:Number;

        public function TestEffectInstance(target:Object)
        {
            super(target);
        }

        override public function play():void {
            super.play();
            createTween(this, [angleFrom, scaleFrom], [angleTo, scaleTo], duration);
        }

        override public function onTweenUpdate(val:Object):void {
            target.rotation = val[0];
            target.scaleX = target.scaleY = val[1];
        }
    }
}

TestEffectInstancescaleX , scaleY の変化を設定するプロパティ scaleFromscaleTo を追加。 そして createTween 実行時に以下のように二つの値のFromとToを配列にして実行。

createTween(this, [angleFrom, scaleFrom], [angleTo, scaleTo], duration);

onTweenUpdate メソッドでは引数valに配列が入るため、val[0], val[1]として使用する。 createTween を呼び出したときの配列と同じ順序で値が格納されているようだ。

ということで前回と全く同じMXMLで以下のようにアニメーションが変更された。

[Flex]カスタムエフェクトの作成 #2

以下のAdobeのサンプルを参考にしながら自分でも作成してみた。

トゥイーンエフェクトについて

ファクトリクラス

TestEffect.as

package
{
    import mx.effects.IEffectInstance;
    import mx.effects.TweenEffect;

    public class TestEffect extends TweenEffect
    {
        public function TestEffect(target:Object=null)
        {
            super(target);
            instanceClass= TestEffectInstance;
        }

        override public function getAffectedProperties():Array{
            return ["rotation"];
        }

        override protected function initInstance(inst:IEffectInstance):void{
            super.initInstance(inst);
            TestEffectInstance(inst).angleFrom = 0;
            TestEffectInstance(inst).angleTo = 360;
        }
    }
}

ファクトリクラスを作成するときに最低限必要な4つのルール

  1. コンストラクタの実装

    コンストラクタ内では必ずsuper()メソッドを実行すること。 引数に受け取ったターゲットオブジェクトも渡す。

  2. instanceClassの設定

    通常はコンストラクタ内で実行する。インスタンスクラスとしてどのクラスを使うかの指定。 Classタイプのオブジェクトを指定するとのこと。(例のようにクラス名をそのまま代入するイメージ)

  3. initInstanceメソッドのオーバーライド

    必ず最初にsuper.initInstance()メソッドを実行する。 引数として受け取ったインスタンスクラスをそのまま渡す。

    また、その他、事前に準備したい自分のエフェクト固有の値などはここでセットする。 例ではインスタンスクラスに回転の開始角度と終了角度を代入している。

  4. getAffectedPropertiesメソッドのオーバーライド

    エフェクトのターゲットとなるオブジェクトに対して、どのパラメータを変更するかを指定する。 しかし、実際にはこのメソッドはなくても動いた。 どういうときに使われるかは不明だが、念のため指定しておいたほうがいいのだろう。 (何かの拍子にエラーが発生したりするのかもしれないため。) 今回はrotationプロパティを変更するため"rotation"という文字列を配列形式で返している。

インスタンスクラス

TestEffectInstance.as

package
{
    import mx.effects.Tween;
    import mx.effects.effectClasses.TweenEffectInstance;

    public class TestEffectInstance extends TweenEffectInstance
    {
        public var angleFrom:Number;
        public var angleTo:Number;

        public function TestEffectInstance(target:Object)
        {
            super(target);
        }

        override public function play():void {
            super.play();
            createTween(this, angleFrom, angleTo, duration);
        }

        override public function onTweenUpdate(val:Object):void {
            target.rotation = val;
        }

        override public function onTweenEnd(val:Object):void {
            super.onTweenEnd(val);
        }
    }
}

ファクトリクラスを作成するときに最低限必要な2+1つのルール

  1. コンストラクタの実装

    受け取ったターゲットオブジェクトを引数にsuper()メソッドを実行するだけでいいみたい。

  2. playメソッドのオーバーライド

    エフェクト開始時に呼び出されるメソッド。今回はTweenEffectInstanceを継承しているため、createTweenメソッドを実行する必要がある。createTweenメソッドの引数は通常以下のようになる。

    createTween(this, 開始値, 終了値 [, アニメーションの継続時間(ミリ秒), アニメーション実行の最小回数(/秒) ] )
    

    慣れないうちは createTween(this, 開始値, 終了値) でいいと思う。

  3. TweenEffectInstance.onTweenUpdateメソッドのオーバーライド

    こちらはTweenEffectを継承したエフェクトを作成しているときのみの制約。 アニメーション更新のタイミングごとに呼び出されるので、ターゲットオブジェクトのプロパティを更新する。 今回の例ではrotationを更新すればよい。 これで回転処理のアニメーションが実行される。

また、TweenEffectInstanceにはonTweenEnd()メソッドのようにアニメーション終了後に呼び出されるメソッドもあり、後処理などが必要な場合は適時オーバーライドすればよい。


ここまでできれば後は以下のようにMXMLファイル内で上記で作ったTestEffectを指定してやればよい。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="400">
    <mx:Button x="150" y="150" label="回転します" width="100" height="100" mouseDownEffect="{TestEffect}"/>
</mx:Application>

できた。 思ってたよりも簡単だ。

[Flex]カスタムエフェクトの作成 #1

なぜこんなことをしようとしているかというと、理由はこちら。

B-Wiki - Flex3勉強会第68回@大阪参加受付 - Flex User Group

※桜をRIAで表現できれば素敵ですね~

これをやってみたいと思ったから。 桜の花びらが舞いながらオブジェクトが消えていくエフェクト。 昔のパチンコ「花満開」とかであった(知らんか)チープな感じでOK。

ってことで一切予備知識なしで調べながら取り組む過程を残していきましょう。

まずはAdobeのドキュメント

基本ですね。 ここです。

Adobe Flex 3 カスタムエフェクトの作成について

ここによると以下の2つのクラスを用意する必要がある。

  1. ファクトリクラス

    mx.effects.Effectもしくはそのサブクラスを継承する。エフェクトのパラメータ(ズームサイズとかエフェクト持続時間など)を受け取り、インスタンスクラスに引き渡す役割を持つ。 以下のようなMXMLで使用しているWipeDownはこのファクトリクラスのことのようだ。 名前も Zoom や Fade などそのエフェクトそのものを表わす名前にする慣例らしい。

    <!-- Define factory class. -->
    <mx:WipeDown id="myWD" duration="1000"/>
    <!-- Assign factory class to effect targets. -->
    <mx:Button id="myButton" mouseDownEffect="{myWD}"/>
    <mx:Button id="myOtherButton" mouseDownEffect="{myWD}"/>
    
  2. インスタンスクラス

    エフェクトの実際の処理を実装するクラス。ファクトリクラスがインスタンスクラスを使用して各コンポーネントのエフェクトを実行していくようなイメージだろう。エフェクト処理そのものは、インスタンスクラス内に書くわけだ。ZoomInstanceやFadeInstanceのように、エフェクト名の末尾にInstanceとつける慣例らしい。


以上、どうやら構成はかなりシンプルだ。 しかし、それだけではない。 ファクトリクラスにしてもインスタンスクラスにしても、目的に応じてある程度の機能を備えたクラスが既に準備されているようだ。

ファクトリクラスには以下の代表的なクラスがある。

クラス名 目的
mx.effects.Effect 単純なエフェクト。つまり一瞬で消えるとか、色が変わるとか、変化に時間の経過を伴わないもの。
mx.effects.TweenEffect アニメーションなどの一定時間再生するエフェクト。ResizeエフェクトなどはこのTweenEffectを継承しているらしい。

今回はアニメーションを必要とするため、TweenEffectを継承して作っていこう。 続く。

「Flex3勉強会第65回@大阪」参加&発表してきた

今回はwonderflのサービスを利用して何か作ってこようという企画。 wonderflは色んなライブラリも使えて、特に3Dとかアニメーションにすごく強いけど、なぜか僕は何も使わず裸一貫で戦ったw

「電流イライラ男」、ぜひ遊んでください。

電流イライラ男 - wonderfl build flash online

製作時間はFlexBuilder使って、5時間くらい。 プレイ時間は7時間くらいw

ソースコードはリンク先から見れるし、修正して機能追加することなんかもできるので、興味があればぜひ。

«前へ || 1 | 2 | 3 | 4 | 5 | 6 || 次へ»

Home > Flash/Flex

Search
Feeds

Page Top