Home

Web 酒 肴

«前へ || 1 | 2 | 3 | 4 |...| 11 | 12 | 13 || 次へ»

[Flex]カスタムエフェクトの作成 #7 - 2直線の交点を求める

久しぶりにこれに取り掛かれる。 5/30のFlex3勉強会第70回@京都までに完成させなくてはいけないので、のんびりしてられないわけだけど、ここにきてまたバンド組んだり知り合いのHP頼まれたり、会社で大がかりな組織変更があってその対応とか、しばらく大変そう・・・。

このエフェクトをきれいにライブラリ化するために、どうしても2つの線分の交点を求める処理が必要になってくる。 どうせならこれも汎用的に使えるようにしようと思って、以下のようなLineクラスを作ることにした。

Lineクラス概要

  • Lineクラスのインスタンスは一つの直線・半直線・線分を表わす
  • (半)直線または線分上にある2つの座標(x1, y1)(x2, y2)によって初期化される
  • 初期化時のパラメータにより、直線・半直線・線分のいずれかを指定することができる
  • 別のLineクラスのインスタンスを引数に取り、お互いが交差している座標を返すgetIntersectionPointメソッドを持つ

これを作成するにあたり、線分の交点を計算するアルゴリズムをいろいろ調べた結果、今回はここを参考にするのが一番目的にあっていそう。

平面幾何におけるベクトル演算 » 直線と線分

あと、今更ながら知って驚いたこと。

ActionScript3ではコンストラクタを複数定義できないっぽい

ちょっと残念。

Nucleus3.41日本語版でアップグレード失敗

Nucleus バージョン3.41 日本語版が登場

これを導入したら、ブログのトップページが表示されなくなってしまった。 データを復旧しようとバックアップファイルからデータベースを再構築したら、それにも失敗。 ここ数ヶ月分のデータが一時的にぶっ飛んでしまった。

冷汗かきながらphpMyAdminを使ってなんとか復旧完了・・・

状況から察するにCustomURL0.3.7が競合を起こしているのかもしれない。 まだ確信はないのでフォーラムなどへの報告はしてないけど。 時間があったら別の環境で検証してみる。

[勉強会]第9回 変なプログラマーの作り方 参加してきた

テーマは開発環境ということで、Flex Builderの紹介とお勧めをしてきました。 プレゼン資料は以下。

ということでよろしくお願いしますw

Adobe Flex Builder Standard 3.0 日本語版 Windows/Macintosh版

[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で以下のようにアニメーションが変更された。

«前へ || 1 | 2 | 3 | 4 |...| 11 | 12 | 13 || 次へ»

Home

Search
Feeds

Page Top