2008.04.02
category
comments

パーティクルクラスを作ってみた

勉強用に自作で作ってみたので公開してみる。4月なので桜の花びらをパーティクルで散らしてみた。使い方はドキュメントクラスに下記のように記述する。

Main.as

ACTIONSCRIPT:
package
{
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import info.five.filters.Particle;
   
    public class Main extends Sprite
    {
        private var p:Particle;
       
        //------------------------------
        //   コンストラクタ
        //------------------------------
        public function Main():void
        {
            this.stage.addEventListener(MouseEvent.CLICK, onClick);
           
            //-----[パーティクル]
            p = new Particle("flower", this);
            p.gravity = -0.2;
            p.wind = -0.5;
        }
       
        private function onClick(e:MouseEvent):void
        {
            p.click();
        }
    }
}

まずはパーティクルインスタンスを作る。第1引数はパーティクルの元となるムービークリップのリンケージ名。第2引数はパーティクルを配置する場所。この場合はステージに配置するのでthisとする。インスタンスを作った後は各パラメータを指定する。gravityはマイナス値でパーティクルが上昇。windはマイナス値で左向きにパーティクルが流れる(0だと円状に広がる)。

p.gravity = -0.2;
p.wind = -0.5;

このサンプルではステージにクリックイベントを登録しているので、リスナー関数の中でp.click()を呼び出すことでクリックタイプのパーティクルを発動することができる。これをp.start()にするとパーティクルが生成され続ける。p.stop()で止める事ができる。

パーティクルクラスはこちら
Particle.as

ACTIONSCRIPT:
package info.five.filters
{
    import flash.display.DisplayObjectContainer;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    import flash.utils.getDefinitionByName;
    import flash.geom.Point;
   
    public class Particle
    {
        private var container:DisplayObjectContainer;
        private var linkage:String;
        private var timer:Timer;
        private var xVel:Number;
        private var yVel:Number;
        private var _vol:Number = 5;
        private var _drag:Number = 0.99;
        private var _shrink:Number = 0.95;
        private var _gravity:Number = 0.8;
        private var _fade:Number = 0.02;
        private var _wind:Number = 0;
       
       
       
        //------------------------------
        //   コンストラクタ
        //------------------------------
        public function Particle(s:String, targetContainer:DisplayObjectContainer)
        {
            linkage = s;
            container = targetContainer;
        }
       
       
        //------------------------------
        //   パーティクルの生成
        //------------------------------
        private function makeParticle(e:Event):void
        {
            for (var i:uint = 0; i <_vol; i++)
            {
                var mc:MovieClip = new(getDefinitionByName(linkage));
                container.addChild(mc);
               
                var mouseX:int = container.stage.mouseX;
                var mouseY:int = container.stage.mouseY;
                var mousePt:Point = new Point(mouseX, mouseY);
                var pt:Point = getNearPoint(mousePt);
               
                mc.x = pt.x;
                mc.y = pt.y;
                mc.xVel = getRandRange( -5, 5);
                mc.yVel = getRandRange( -5, 5);
                mc.rot = getRandRange( -20, 20);
               
                mc.addEventListener(Event.ENTER_FRAME, onRender);
            }
        }
       
       
        //------------------------------
        //   指定範囲からランダム値の取得
        //------------------------------
        private function getRandRange(min:Number, max:Number):Number
        {
            var randomNum:Number = (Math.random() * (max - min )) + min;
            return randomNum;
        }
       
       
        //------------------------------
        //   近似ポイント座標の取得
        //------------------------------
        private function getNearPoint(pt1:Point):Point
        {
            var len:uint = Math.round(Math.random() * 10);
            var angle:uint = Math.round(Math.random() * 2 * Math.PI);
            var pt2:Point = Point.polar(len, angle);
           
            pt2.offset(pt1.x, pt1.y);
            return pt2;
        }
       
       
        //------------------------------
        //   スタート
        //------------------------------
        public function start():void
        {
            container.stage.addEventListener(Event.ENTER_FRAME, makeParticle);
        }
       
       
        //------------------------------
        //   ストップ
        //------------------------------
        public function stop():void
        {
            container.stage.removeEventListener(Event.ENTER_FRAME, makeParticle);
        }
       
       
        //------------------------------
        //   クリック
        //------------------------------
        public function click():void
        {
            container.stage.addEventListener(Event.ENTER_FRAME, makeParticle);
           
            timer = new Timer(300, 1);
            timer.addEventListener(TimerEvent.TIMER, onDelay);
            timer.start();
        }
       
        private function onDelay(e:TimerEvent):void
        {
            container.stage.removeEventListener(Event.ENTER_FRAME, makeParticle);
            timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onDelay);
        }
       
       
        //------------------------------
        //   レンダー
        //------------------------------
        private function onRender(e:Event):void
        {
            var mc:MovieClip = e.target as MovieClip;
           
            if (mc.alpha> 0)
            {
                mc.x += mc.xVel + _wind * 10;
                mc.y += mc.yVel;
               
                mc.xVel *= _drag;
                mc.yVel *= _drag;
               
                mc.scaleX *= _shrink;
                mc.scaleY *= _shrink;
               
                mc.yVel += _gravity;
                mc.alpha -= _fade;
                mc.rotation += mc.rot;
            }
            else
            {
                mc.removeEventListener(Event.ENTER_FRAME, onRender);
                mc.removeChildAt(0);
                container.removeChild(mc);
                mc = null;
            }
        }
       
       
        //------------------------------
        //   getter setter
        //------------------------------
        public function get vol():Number { return _vol; }
       
        public function set vol(value:Number):void {
            _vol = value;
        }
       
        public function get drag():Number { return _drag; }
       
        public function set drag(value:Number):void {
            _drag = value;
        }
       
        public function get shrink():Number { return _shrink; }
       
        public function set shrink(value:Number):void {
            _shrink = value;
        }
       
        public function get gravity():Number { return _gravity; }
       
        public function set gravity(value:Number):void {
            _gravity = value;
        }
       
        public function get fade():Number { return _fade; }
       
        public function set fade(value:Number):void {
            _fade = value;
        }
       
        public function get wind():Number { return _wind; }
       
        public function set wind(value:Number):void {
            _wind = value;
        }
    }
}

不具合があれば、指摘していただけると嬉しいです。

2008.03.11
category
comments

katamariを真似してうねうね動く円

うねうねと有機的に動く円を作ってみた。katamariさんのサイトを見よう見真似で模写。なんか違う気がするなぁ。うねり具合がしっくりこない。

2008.03.05
category
comments

パーティクルの練習

パーティクルのお勉強中。こうやって動かしてたのねー。

2008.01.16
category
comments

コリンムックのAS3勉強会に行ってきた

dsc_1795.jpg

今更ですが2008年最初のエントリー。Essential ActionScript3.0の著者コリンムックの勉強会に行ってきた。世界トップクラスのActionScriptのインストラクターという事でかなり期待して参加したけど、その名の通り初心者にも分かりやすい内容だった。オブジェクト指向プログラミングを独学で勉強中の身としてはかなり役立った。今まで曖昧に覚えてた事が理解できてきたかも。人のコーディングを見れると言うのは、貴重だし理解も早い。

内容としてはVirtualZooという架空の動物園でペットを飼うゲームをAS3で作る、というもの。(たまごっちの簡易版)エサをやらないと10秒で死んでしまうライフゲーム。このゲームをオブジェクト指向プログラミングで作るとどうなるかをライブコーディングしてくれました。特に感じたのがAS3を意識しないでOOPを学べた事。AS3の文法でがっつりコーディングするのではなく、OOP的考え方を教えたかったようだ。以下は自分的に気になったメモ。

・ソースは/virtualzoo/src/フォルダに格納。
・素材は/virtualzoo/bin/フォルダに格納。
・メインクラスは必ずSpriteかMovieClipを継承。
・変数にnullを代入すると参照を切った状態(メモリには残ったまま)になる。これで削除の準備状態に。ガーベジコレクション発動でメモリから解放。
・this参照を省略するとローカル変数→インスタンス変数の順番でFlash側が自動的に参照先を探してくれる。省略すると実行速度が多少落ちたりするのかな?
・MODEL(ロジック、データ管理、表示はしない)
・VIEW(グラフィック、レンダリング、MODELから通知を受けたら画面更新)
・CONTROLLER(ユーザからの入力、インプット、MODEL・VIEWに通知)
・EventDispatcherクラス。任意のタイミングでカスタムイベントを発行。ようやく便利さに気づいた。
・setメソッド、getメソッド。インスタンス変数を直接参照せずにメソッドで呼び出したり、値を代入する。
・継承について。
・状態変数の使い方。public static const PETSTATE_FULL:int = 0;
・container.mouseEnabled = false; 自身のオブジェクトがマウスイベントを受け取らなくなる。
・container.mouseChildren = false; オブジェクトの子供インスタンスにマウスイベントが送信されなくなる。

本日の資料が公開されてます。

2007.12.12
category
comments

AS3のFlvPlayBackを試してみる

AS2の時とコードの記述が少し変わったみたい。ポイントはメタデータの取得方法とムービー状態(再生・停止・シークなど)をどう捕まえるか。メタデータに関してはMetadataEventのMETADATA_RECEIVED をリスナーに登録しておいて、受け取るハンドラの中でそれぞれ取り出してやればよい(左上の白文字)。次にムービー状態に関してはVideoEventに捕まえたいイベントオブジェクトを登録しておくだけ。あとはハンドラ内でムービーの再生時間などを処理してやればよい。playheadPercentageを使うと、現在の再生ヘッドが全体の何パーセントなのかを示してくれる。トータルタイムにこのパーセンテージを乗算すれば現在の再生時間が簡単に出せる。

デフォルトのシークバーコンポーネントには再生ヘッドまでのプログレス表示をしてくれない。今回はシークバーと同じ幅のムービークリップをあらかじめシークバーコンポーネントの中に配置しておいてscaleXにplayheadPercentageの値を入れて動作させてみた(シークバーの白い部分)。

ACTIONSCRIPT:
package
{
    import flash.display.MovieClip;
    import fl.video.MetadataEvent;
    import fl.video.FLVPlayback;
    import fl.video.VideoEvent;
    import flash.text.TextField;
   
   
    public class Main extends MovieClip
    {
        //------------------------------
        //    コンストラクタ
        //------------------------------
        public function Main()
        {
            player.source = "test.flv";
            player.bufferTime = 1;
           
            player.addEventListener(MetadataEvent.METADATA_RECEIVED, onMetadata);
            player.addEventListener(VideoEvent.READY, onReady);
            player.addEventListener(VideoEvent.PLAYING_STATE_ENTERED, onPlaying);
            player.addEventListener(VideoEvent.PLAYHEAD_UPDATE, onPlayHead);
            player.addEventListener(VideoEvent.PAUSED_STATE_ENTERED, onPause);
            player.addEventListener(VideoEvent.COMPLETE, onComplete);
           
            player.playPauseButton = playBtn;
            player.muteButton = muteBtn;
            player.backButton = backBtn;
            player.seekBar = seekBar;
            player.fullScreenButton = fullScreenBtn;

        }
       
       
        //------------------------------
        //    メタデータの取得
        //------------------------------
        private function onMetadata(e:MetadataEvent):void
        {
            meta.appendText("FrameRate = " + player.metadata.framerate + "\n");
            meta.appendText("VideoDataRate = " + player.metadata.videodatarate + "\n");
            meta.appendText("Height = " + player.metadata.height + "\n");
            meta.appendText("Width = " + player.metadata.width + "\n");
            meta.appendText("Duration = " +player.metadata.duration + "\n");
        }
       
        //------------------------------
        //    FLVの初期化
        //------------------------------
        private function onReady(e:VideoEvent)
        {
            var time = String(Math.floor(player.totalTime));
            var minute = String(Math.floor(time / 60));
            var second = String(time % 60);
           
            if (minute <10)
            {
                minute = "0" + minute;
            }
           
            if(second <10)
            {
                second = "0" + second;
            }
           
            total.text = "/ " + minute + ":" + second;
            now.text = "00:00";
            stateText.text = "PAUSE";
           
            seekBar.seekProgress.scaleX = 0;
        }
       
       
        //------------------------------
        //    FLVの再生時
        //------------------------------
        private function onPlaying(e:VideoEvent)
        {
            stateText.text = "PLAY";
        }
       
       
        //------------------------------
        //    FLVの再生中
        //------------------------------
        private function onPlayHead(e:VideoEvent)
        {
            var time = Math.floor(player.totalTime * player.playheadPercentage / 100);
            var minute = Math.floor(time / 60);
            var second = time % 60;
           
            if (minute <10)
            {
                minute = "0" + minute;
            }
           
            if(second <10)
            {
                second = "0" + second;
            }
           
            now.text = minute + ":" + second;
           
            //-----[再生ヘッドまでのプログレスバー]
            seekBar.seekProgress.scaleX = player.playheadPercentage / 100;
        }
       
       
        //------------------------------
        //    FLVの一時停止時
        //------------------------------
        private function onPause(e:VideoEvent)
        {
            stateText.text = "PAUSE";
        }
       
       
        //------------------------------
        //    FLVの再生終了時
        //------------------------------
        private function onComplete(e:VideoEvent)
        {
            stateText.text = "FINISH";
        }
    }
}

2007.11.21
category
comments

ランダムテキストをやってみる

最近ビットマップフォントの常套モーションになっているランダムテキストを自作してみた。最初のモーションですべての文字を「アンダーバー」に変えてからランダムに1文字づつシャッフルしてます。TimerEventで動かしてるんだけど、シャッフルのスピードがブラウザで見るとやや遅い。デバッグプレイヤーだと思い通りのスピードなのに。ENTER_FRAMEで動かすほうが良いの?。誰か教えてください。

コードは以下参照。シャッフル時間を任意に調整できるようにしてます。

ACTIONSCRIPT:
package info.five.filters
{
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
   
    public class RandomText
    {
        const _replaceWord:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=+-*/!?";
        private var _content:TextField;
        private var _word:String;
        private var _timer:Timer;
        private var _randomCnt:uint;
        private var _randomArr:Array = new Array();
        private var _shuffleArr:Array = new Array();
        private var _speed:Number = 1;
        private var _time:uint = 30;
        private var _cnt1:uint = 0;
        private var _cnt2:uint = 0;
       
       
        //------------------------------
        //   ランダムテキストクラス
        //------------------------------
        public function RandomText(content:TextField)
        {
            this._content = content;
            this._word = content.text;
           
            _timer = new Timer(_speed, 0);
            _timer.addEventListener(TimerEvent.TIMER, onTick, false, 0, true);
        }
       
       
        //------------------------------
        //   テキストの再設定
        //------------------------------
        public function setText(newText:String):void
        {
            this._word = newText;
        }
       
       
        //------------------------------
        //   モーションスタート
        //------------------------------
        public function start():void
        {
            //-----[変数の初期化]
            _content.text = "";
            _randomCnt = 0;
            _randomArr = [];
            _shuffleArr = [];
            _cnt1 = 0;
            _cnt2 = 0;
           
            _content.wordWrap = false;
            _content.autoSize = TextFieldAutoSize.LEFT;
           
            //-----[文字番号を配列に取得]
            for(var i:uint=0; i<_word .length; i++)
            {
                _randomArr.push(i);
                _content.appendText("_");
            }
           
            //-----[文字番号をシャッフル]
            _shuffleArr = shuffle(_randomArr);
           
            _timer.start();
        }
       
        //------------------------------
        //   配列のシャッフル
        //------------------------------
        private function shuffle(arr:Array):Array
        {
            var l = arr.length;
            var newArr = arr;
           
            while(l)
            {
                var m = Math.floor(Math.random()*l);
                var n = newArr[--l];
                newArr[l] = newArr[m];
                newArr[m] = n;
            }
            return newArr;
        }
       
       
        //------------------------------
        //   シャッフルモーション
        //------------------------------
        private function onTick(e:TimerEvent):void
        {
            if(_randomCnt <_time)
            {
                var randomNo:uint = Math.round(Math.random() * _replaceWord.length);
               
                var s1:uint = _shuffleArr[_cnt1];
                var s2:uint = _shuffleArr[_cnt1] + 1;
                var s3:String = _replaceWord.charAt(randomNo);
               
                _content.replaceText(s1, s2, s3);
               
                if(_cnt1>= _word.length - 1) _cnt1 = 0;
                else _cnt1++;
            }
            else
            {
                var s4:uint = _shuffleArr[_cnt2];
                var s5:uint = _shuffleArr[_cnt2] + 1;
                var s6:String = _word.charAt(_shuffleArr[_cnt2]);
               
                _content.replaceText(s4, s5, s6);
                _cnt2++;
               
                if(_cnt2>= _word.length)
                {
                    _timer.stop();
                }
            }
            _randomCnt++;
        }
       
       
        //------------------------------
        //    getter
        //------------------------------
        public function get time():uint
        {
            return _time;
        }
       
       
        //------------------------------
        //    setter
        //------------------------------
        public function set time(t:uint):void
        {
            _time = t;
        }
    }
}

使い方はテキストフィールドを引数にインスタンスを作ってstart()メソッドを呼ぶだけ。

ACTIONSCRIPT:
import info.five.filters.RandomText;

var randomText1:RandomText = new RandomText(myText1);
randomText1.start();

シャッフルスピードを変えるにはtimeプロパティを変える。(デフォルトは30)

ACTIONSCRIPT:
randomText1.time = 100;

テキストを変更するには、setText()メソッドの引数に文字列を与える。

ACTIONSCRIPT:
randomText1.setText("HOGE")
randomText1.start();

2007.10.05
category
comments

QueueLoaderを使って複数画像をまとめて読み込む

QueueLoaderを使うとCASAのGroupLoadみたいに複数の画像やswf、mp3をまとめて読み込むことが出来る。簡単に説明すると、_oLoaderにaddItem()で画像等を追加した後、まとめて_oLoader.execute()で読み込みを実行するだけ。このクラスの中では各画像の読み込みを「アイテム」、トータルの読み込みを「キュー」と定義しているみたい。

_oLoader.addItem()の第1引数は画像のパス、第2引数は読込み先のオブジェクト、そして第3引数はtitleというプロパティの値を設定できる。クラス内部を見てみるとtitleというプロパティだけが存在していたので他のプロパティは付け加えられないように設計されてるようだ。ということは、この値を使って各画像のローディング状態を読込み先のムービークリップに渡してローディングバーとかに反映できるのか。これは使える。

ACTIONSCRIPT:
import com.hydrotik.utils.QueueLoader;
import com.hydrotik.utils.QueueLoaderEvent;

//-----[初期設定]
var _oLoader:QueueLoader = new QueueLoader();
var imageNum:uint = 4;

//-----[読込画像を追加]
for(var i:uint = 0; i<imageNum; i++)
{   
    var image:Sprite = new Sprite();
    image.x += 10;
    addChild(image);
   
    _oLoader.addItem("images/"+i+".jpg", image, {title:"Image "+i});
}

//-----[イベントリスナーの登録]
_oLoader.addEventListener(QueueLoaderEvent.QUEUE_START, onQueueStart, false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.ITEM_START, onItemStart, false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.ITEM_PROGRESS, onItemProgress, false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.ITEM_INIT, onItemInit, false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.ITEM_ERROR, onItemError,false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.QUEUE_PROGRESS, onQueueProgress, false, 0, true);
_oLoader.addEventListener(QueueLoaderEvent.QUEUE_INIT, onQueueInit, false, 0, true);

//-----[読込の開始]
_oLoader.execute();

function onQueueStart(event:QueueLoaderEvent):void
{
    trace("キューの読込開始");
}

function onItemStart(event:QueueLoaderEvent):void
{
    trace(event.title);
    trace("アイテムの読込開始");
}

function onItemProgress(event:QueueLoaderEvent):void
{
    trace("アイテムの読込中: "+Math.round(event.percentage * 100)+ "%");
}

function onQueueProgress(event:QueueLoaderEvent):void
{
    trace("キューの読込中: "+Math.round(event.queuepercentage * 100)+ "%");
}

function onItemInit(event:QueueLoaderEvent):void
{
    trace("アイテムの読込完了");
}

function onItemError(event:QueueLoaderEvent):void
{
    trace("エラー");
}

function onQueueInit(event:QueueLoaderEvent):void
{
    trace("キューの読込完了");
}

Thank you for Adams.

2007.10.03
category
comments

ASQLを使ってas3から直接MySQLとやり取りしてみる

久しぶりのas3エントリー。ASQLとはMySQLに直接接続できるライブラリー。現在の最新リリースはalpha 0.1.4。まずはクラスをASQLのサイトからダウンロードしてくる。ソースは以下参照。

今回はローカルにあるMySQLに接続するので、connector.connect()の第1引数と第2引数はそのままにしておく。第3引数と第4引数をデータベースに合わせて変更する。第5引数はポート番号なので特に変更する必要なし。次にconnector.query();でSQL文を発行してやる。ここも任意のテーブル名を指定する。最後に受け取るデータはすべて配列で返ってくるため、evt.data[0].columnNameとする事で個別に取得できる。columnNameはデータベースのフィールド名に置き変えてやれば、それに対応するデータが取れる。用事が済んだらconnector.disconnect();でコネクションを開放してやるのもお忘れなく。

本家サイトのフォーラムでも触れられてるけど、swfをデコンパイルされるとサーバー情報が丸見えになるのでプロテクトをかけるなどの注意が必要。

ACTIONSCRIPT:
package
{
   import pl.mooska.asql.*;
   import pl.mooska.asql.events.*;
   import flash.display.Sprite;

   public class AsqlSample extends Sprite
   {
      private var connector:Asql = new Asql();

      public function AsqlSample()
      {
         connector.addEventListener( SQLEvent.CONNECT, handleConnect );
         connector.addEventListener( SQLError.SQL_ERROR, handleError );
         connector.addEventListener( SQLEvent.SQL_OK, handleOK );
         connector.addEventListener(SQLEvent.SQL_DATA, handleData);
         connector.connect("localhost", "root", "パスワード", "データベース名" , 3306);
      }
     
      private function handleConnect ( evt:SQLEvent ):void
      {
         trace("コネクション成功");
         connector.query("select * from テーブル名");   //type a query, without semicolon at the end
      }
      private function handleError ( evt:SQLError ):void
      {
          trace("エラー "+evt.text);
      }
      private function handleOK ( evt:SQLEvent ):void
      {
         trace("SQL文の実行成功");
      }
      private function handleData ( evt:SQLEvent ):void
      {
         trace("データ受け取り成功");
         trace( evt.data[0].columnName);
         connector.disconnect();
      }
   }
}

2007.06.15
category
comments

外部画像を読み込んでスムージング

画像の読み込みをリスナーで捕まえるにはcontentLoaderInfoを使う。スムージングをかけるには読み込み完了時にビットマップ化して、スムージングプロパティをtrueにする。回転させるとスムージングがかかってるのが良く分かる。

ACTIONSCRIPT:
var imgLoader:Loader = new Loader();
imgLoader.load(new URLRequest("画像のurl"));
imgLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);

//-----[読み込み中イベントの処理]
function onProgress(e:Event):void
{
    trace("progress = " + Math.round((e.bytesLoaded * 100) / (e.bytesTotal)));
}

//-----[読み込み完了イベントの処理]
function onComplete(e:Event):void
{
    //-----[スムージング処理]
    var loadedImage:Bitmap = Bitmap(imgLoader.content);
    loadedImage.smoothing = true;
    addChild(loadedImage);
    loadedImage.rotation = 15;
}

2007.06.10
category
comments

ムービークリップのカーソルアイコンについて

AS3ではMouseEventをリスナーに登録しても標準では指アイコンに変化しない。アイコンを変える時は明示的に設定してやる必要あり。

ACTIONSCRIPT:
mc.buttonMode = true;
mc.useHandCursor = true;

buttonModeをtrueにしておかないとuseHandCursorが効かないのだ。ちょっとハマったのでメモ。

page 2 / 3«123»