サイブリッジラボでタグ「FLASH」が付けられている記事一覧

[Flash]Flash liteのファイルサイズ軽量化Flash

とくながです。

サイブリッジはソーシャルアプリの開発にも力を入れており
最近は新しいアプリのリリースも近づいているため、
Flash liteに触れる機会もより多くなっています。

前回のエントリーでも簡単に触れましたが、
Flash liteはドコモ機種の仕様で100kbを超える
SWFが読めないので、大規模なアプリとなると
いかにファイルサイズを小さくするかが鍵となります。

今回はファイルサイズを軽量化する方法をまとめます。

読み込む画像の下げ、容量の削減

読み込む画像などのダイエットは最低限必要です。
またビットマップするかベクターにするか、
状況に応じて都度確認する必要があります。

複雑な画像はライブラリから「写真画質(JPEG)」、
シンプルな画像は「可逆圧縮(PNG)」にすることで
容量を多く削減することができます。

パスの量を減らす

イラレから持ってきたデータなどで発生しやすいですが、
パスが多いデータについては削減する必要があります。
「修正」→「シェイプ」→「最適化」で調整は可能ですが、
データによっては荒くなるので注意が必要です。

2回以上出現するオブジェクトはインスタンス化

インスタンス化したオブジェクトはいくつページに出現しても
大幅に容量が増えることはありませんので、
全てのオブジェクトをインスタンス化する気持ちで良いと思います。

色やサイズを変更しても、容量はほとんど増えません。

ループするアニメーションはムービークリップ

トゥイーンを使ってアニメーションを行う場合、
インスタンスのタイプを「グラフィック」にして
ループ処理を行うという方法がありますが、
これだとフレームの数だけ容量が増大してしまいます。

ムービークリップの中はstop();しない限り、
自動的にループするという特徴がありますので、
できるだけグラフィックでのアニメーションは控えましょう。

変数名やラベル名はできるだけ短く

塵も積もれば山となりますので、
できるだけ短い変数名で記載するクセをつけましょう

[Flash]SharedObjectでデータ管理のコツFlash

とくながです。

先日Adobe CS5が発表されて話題騒然ですね!
Photoshopの新機能に関しては、魔法の域に達しているようですw

さて、最近Flashに触る機会が増えてきました。
前回、前々回とSharedObjectに関するエントリーを書かせて頂きましたが、
今回はSharedObjectでデータ管理のコツを紹介しようかと思います。

SharedObjectのデータ管理って?

SharedObjectはFlash版Cookieとも呼ばれているとおり、
ユーザーのローカルに変数などのデータを格納することができます。
ローカル内にデータを保存することでDBサーバなどを必要としませんが、
逆に言えば細かい仕様の変更に対応し難いというデメリットがあります。

例えばアップデート後に致命的なバグなどを発見し、
「一部の変数部分だけ前のバージョンに戻したい」
というケースもにあるかと思いますが、
SharedObjectにそのような機能はありません。

本来ならswfをアップデートするだけで済むような問題でも、
バグ発覚前にアクセスしたユーザのローカルには既に不正な値が
保存されてしまった為、それを修正する必要があるからです。

1:バックアップファイルを用意する

おそらく一番シンプルで簡単な方法は、
過去バージョンのバックアップを用意することではないでしょうか。

「save.sol」とは別に「save_log.sol」というファイルを生成させ、
万が一バグなどが発生した場合に戻すという単純な方法ですが
多少データが古くなったり、保存容量が大きい場合難しいですね。
(SharedObjectの保存容量はデフォルトで100Kbです)

2:SharedObjectにバージョン情報を持たせておく

例えば2010年6月にアクセスして来たユーザには、
「version = "2010_06";」のような変数を持たせることで
アップデート前後のフラグとして使うことができます。

仮に、2010年5月以前にアクセスしたユーザの変数hogeが不正だとして
一旦この変数だけをリセットしたい場合、このように書くだけでOKになります。
var so = SharedObject.getLocal("save" , "/");
if (so.data.version != "2010_06") { /* 保存時期が2010年6月より前の場合 */
  delete so.data.hoge; /* 不正な値を削除 */
  so.data.version = "2010_06"; /*バージョンを2010年6月へ変更*/
}
たったこれだけですが、保存時期を特定したコントロールが可能になります。

番外編:別のswfファイルからSharedObjectを参照する

上記とは少し違う話になりますが、/main/sample.swfで保存したデータを
/test/sample.swfで読み込む場合、getLocalの第二引数(ディレクトリ指定)
を省略してしまうと面倒になってしまうので注意が必要です。
▼第二引数を省略した場合の保存先(lab.cybride.jp)
/main/sample.swf・・・「/lab.cybridge.jp/main/save.sol」に保存される。
/test/sample.swf・・・「/lab.cybridge.jp/test/save.sol」に保存される。
このように、SharedObject内でもディレクトリごとに分けて保存されますが、
SharedObjectへの参照は、上のディレクトリ以外を指定することができません!
例えば/test/sample.swfから「/lab.cybridge.jp/main/save.sol」は読み込めません。
よほどの理由が無い限り、保存場所はルートを指定しておきましょう!

[Flash]SharedObjectの値をローカルで確認・編集するFlash

とくながです。

個人的に非常に期待していたFlash for iPhoneですが、
iPhone向けFlashアプリ変換ツールへの投資中止」という
なんとも残念な結果になってしまいました。

iPhoneアプリはこれからも大きくシェアを広げていくと考えていて、
もちろん弊社でも水面下でプロジェクトが進行中なのですが、
FlashでiPhoneアプリを作る!という方法が実現できず残念に思います。

そんなわけで前回、SharedObjectで楽々データ保存という
エントリーを書かせて頂きましたが、ここで保存されている変数を、
実際に確認したり編集することができる方法をご紹介します。

※前回ご紹介したアクセスカウンター


SharedObjectのデータを書き換えると...

SharedObjectはFlash版Cookieとも呼ばれているとおり、
ユーザーのローカルに変数などのデータを格納することができます。
しかし、正確な値が格納されているかを調べたり、
値を変えてサクッと動作テストをする機能が標準にはありません。

格納されてる値をtrueからfalseに変えて動作テストをする時など、
わざわざFlash上で値を変えて保存させて、では効率が悪すぎます。
そこで、ローカル上に保存されている値を直接参照・書き換えてしまい、
Flash側での保存機能は利用せずに、好きな値を送ってしまいましょう。

あまり実例を見かけたことはないですが、特にFlashゲームなどでは
不正行為をすることも可能だったりしますので、悪用は厳禁ですよ。

SharedObjectで保存したデータ(solファイル)の格納場所

SharedObjectで保存したデータは、下記に格納されています(Windows)
C:\Documents and Settings\ユーザ名\Application Data\Macromedia\Flash Player\#SharedObjects
中にIDのようなディレクトリが入っていると思いますが、
その中に、solファイルが各ドメインごとに分かれて格納されています。
たとえば、labs.cybridge.jpディレクトリの中を覗くと、
「lab20100226.sol」というファイル名が見つかるかと思います。
la.jpg

.sol Editorでローカル変数を操作

データを保存場所が分かったら、早速情報を書き換えてみましょう。
今回使用するのは海外製の.sol Editorというソフトウェアです。

インストールした後、さっきのsolファイルを開いてみてください。
counterという変数の中に、アクセスした数が保存されています。
tok.jpg
 
ここの数値をたとえば100とかに変えてしまうと、あら不思議!
シカータ君が間違って101回目とカウントしてしまうようです。

▼カウンターが101回目になりました!
la2.jpg
アクセスカウンタ程度なら何の問題もありませんが、
特にゲームなどでSharedObjectを使っている方はご用心。
不正行為対策にもなりますので、ご活用されては如何でしょうか。

続・フラクタルノイズの雲Flash

こんにちは。逆から読んでもきつつきです。


前回、フラクタルノイズを用いた雲をFLASHで描画するサンプルを掲載いたしました。

今回はこちらを改良し、時間経過とともに雲が変化していくものを作成してみました。


・・・なんか速いですね。。フレームレートを落とせば調整できると思います。

改良箇所は5カ所。まずは変数の追加です。

        public var CloudDataV:Array;    // 乱数速度テーブル
        public var CloudDataA:Array;    // 乱数加速度テーブル

次に、init関数にタイマー処理を追加します。

        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
           
            // 背景色との加算合成
            this.blendMode = BlendMode.ADD;

            // BitmasDataの初期化
            screen = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0xFF000000 );
            addChild( new Bitmap( screen ) );

            // 雲データの初期化
            initCloud();

            var timer:Timer = new Timer(1, 0);
            timer.addEventListener(TimerEvent.TIMER, refresh);
            timer.start();
           
        }

タイマー処理に伴い、新たにコールバック関数を一つ追加します。

        private function refresh(e:TimerEvent):void
        {
            renderCloudData();
        }

雲の初期化関数initCloudに、速度テーブルと加速度テーブルの初期化処理を加えます。

        private function initCloud():void {
           
            // 配列の確保+乱数テーブルの作成
            CloudData = new Array(SIZE + 1);
            CloudDataD = new Array(SIZE + 1);
            CloudDataV = new Array(SIZE + 1);
            CloudDataA = new Array(SIZE + 1);
           
            var i:int;
            var j:int;
            for (i = 0; i < SIZE + 1;++i)
            {
                CloudData[i] = new Array(SIZE + 1);
                CloudDataD[i] = new Array(SIZE + 1);
                CloudDataV[i] = new Array(SIZE + 1);
                CloudDataA[i] = new Array(SIZE + 1);
               
                for (j = 0; j < SIZE + 1;++j) {
                    CloudDataD[i][j] = (Math.random()*2-1.0);
                    CloudDataV[i][j] = 0;
                    CloudDataA[i][j] = 0;
                }
            }
           
            // 乱数テーブルから雲データの作成
            updateCloud();
        }

最後に、renderCloudData関数の変数宣言直後に雲の更新処理を加えます。

            // 雲の更新
            for (i = 0; i < SIZE+1;++i) {
                for (j = 0; j < SIZE+1;++j ) {
                   
                    // 加速度をランダムに変化
                    CloudDataA[i][j] += (Math.random() * 2 - 1.0);
                    CloudDataA[i][j] *= 0.9;
                   
                    // 速度に加速度を加算
                    CloudDataV[i][j] += CloudDataA[i][j];
                    CloudDataV[i][j] *= 0.9;
                   
                    // 速度を乱数テーブルに加算
                    CloudDataD[i][j] += CloudDataV[i][j];
                    CloudDataD[i][j] *= 0.9;
                }
            }

            updateCloud();

処理の内容としては、雲の濃度値を変化させるために、濃度値の1フレームあたりの変化量(速度)と、変化量の変化量(加速度)を設けて、乱数テーブルを毎フレーム変化させています。

FLASH的な部分としては、タイマーによるビットマップデータオブジェクトの描画アニメーションのサンプルです。無駄なフラクタルノイズで着飾ってます。

フラクタルノイズの雲Flash

こんにちは。逆から読んでもきつつきです。


最近、やっとFLASHに目覚め初めています。
そんなわけで今回はActionScript3.0の話題ですが、勉強中の適当なサンプルを掲載しても面白くないので、学生時代に一度書いたことのあるフラクタルノイズの雲をAS3で書いてみました。



読み込むたびに模様が変わります。
以下、サンプルコードです。

    package
    {
        import flash.display.*;
        import flash.events.*;
        import flash.geom.*;
        import flash.utils.Timer;

        [SWF(backgroundColor="#3333cc", frameRate="60",width="128",height="128" )]
        /**
         * ...
         * @author 啄木鳥@cybridge.jp
         */
        public class Main extends Sprite
        {
            public const SIZE:Number = 128;
            public var screen:BitmapData;

            public var CloudData:Array;        // 濃度
            public var CloudDataD:Array;    // 乱数テーブル
           
            public function Main():void
            {
                if (stage) init();
                else addEventListener(Event.ADDED_TO_STAGE, init);
            }
           
            private function init(e:Event = null):void
            {
                removeEventListener(Event.ADDED_TO_STAGE, init);
                // entry point
               
                // 背景色との加算合成
                this.blendMode = BlendMode.ADD;

                // BitmasDataの初期化
                screen = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0xFF000000 );
                addChild( new Bitmap( screen ) );

                // 雲データの初期化
                initCloud();

                // 描画
                renderCloudData();
            }
           
            private function initCloud():void {
               
                // 配列の確保+乱数テーブルの作成
                CloudData = new Array(SIZE + 1);
                CloudDataD = new Array(SIZE + 1);
               
                var i:int;
                var j:int;
                for (i = 0; i < SIZE + 1;++i)
                {
                    CloudData[i] = new Array(SIZE + 1);
                    CloudDataD[i] = new Array(SIZE + 1);
                   
                    for (j = 0; j < SIZE + 1;++j) {
                        CloudDataD[i][j] = (Math.random()*2-1.0);
                    }
                }
               
                // 乱数テーブルから雲データの作成
                updateCloud();
            }
           
            private function updateCloud():void{

                // 初期の4点を求める
               
                CloudData[0][0] = CloudDataD[0][0] * SIZE ;
                CloudData[0][SIZE] = CloudDataD[0][SIZE] * SIZE ;
                CloudData[SIZE][0] = CloudDataD[SIZE][0] * SIZE ;
                CloudData[SIZE][SIZE] = CloudDataD[SIZE][SIZE] * SIZE ;
               
                // 再帰処理で残りの点を求める
                updateCloud_r(SIZE / 2);
            }
           
            private function updateCloud_r(level:int):void {

                // 再帰処理脱出
                if (level == 0)
                {
                    return;
                }
               
                var i:int;
                var j:int;

                // 前に計算した4点の中間点を求める
                for (i = level; i < SIZE; i += level*2 )
                {
                    for (j = level; j < SIZE; j += level*2 ) {
                        CloudData[i][j] = (CloudData[i - level][j - level] + CloudData[i - level][j + level] + CloudData[i + level][j - level] + CloudData[i + level][j + level]) / 4 + (CloudDataD[i][j] * level);
                    }
                }
               
                // 前に計算した2点と↑で計算した2点から中間点を求める
                var count:int;
                var c:Number;

                for (i = level; i < SIZE; i += level * 2 ) {
                    for (j = 0; j < SIZE + 1; j += level * 2 )
                    {
                        count = 0;
                        c = 0;
                        if (checkPoint(i - level, j)) {
                            ++count;
                            c += CloudData[i - level][ j];
                        }

                        if (checkPoint(i + level, j)) {
                            ++count;
                            c += CloudData[i + level][ j];
                        }

                        if (checkPoint(i, j - level)) {
                            ++count;
                            c += CloudData[i][ j - level];
                        }

                        if (checkPoint(i, j + level)) {
                            ++count;
                            c += CloudData[i][ j + level];
                        }
                       
                        CloudData[i][j] = c / count + (CloudDataD[i][j] * level);
                    }
                }
               
                for (i = 0; i < SIZE + 1 ; i += level * 2 )
                    for (j = level; j < SIZE; j += level * 2 ) {
                    {
                        count = 0;
                        c = 0;

                        if (checkPoint(i - level, j)) {
                            ++count;
                            c += CloudData[i - level][ j];
                        }

                        if (checkPoint(i + level, j)) {
                            ++count;
                            c += CloudData[i + level][ j];
                        }

                        if (checkPoint(i, j - level)) {
                            ++count;
                            c += CloudData[i][ j - level];
                        }

                        if (checkPoint(i, j + level)) {
                            ++count;
                            c += CloudData[i][ j + level];
                        }
                       
                        CloudData[i][j] = c / count+ (CloudDataD[i][j]*level);
                    }
                }
               
                // 再帰呼び出し
                updateCloud_r(level / 2);
            }
           
            // 座標が適切かどうか調べる
            private function checkPoint(i:int, j:int):Boolean
            {
                if (0 <= i && i < SIZE + 1 && 0 <= j && j < SIZE + 1) {
                    return true;
                }
               
                return false;
            }
           
            // レンダリング
            private function renderCloudData():void
            {
                var i:int;
                var j:int;
                var c:int;
                var c_max:Number;
                var c_min:Number;
                var d:Number;

                c_max = CloudData[0][0];
                c_min = -CloudData[0][0];

               
                for (i = 0; i < SIZE;++i) {
                    for (j = 0; j < SIZE;++j ) {
                        c_max = Math.max(c_max, CloudData[i][j]);
                        c_min = Math.min(c_min, CloudData[i][j]);
                    }
                }
               
                d = c_max - c_min;

                screen.lock();
               
                for (i = 0; i < SIZE;++i) {
                    for (j = 0; j < SIZE;++j ) {
                        c = 255 * (CloudData[i][j] - c_min) / d;
                        screen.setPixel32(i, j, (0xff000000 | (c<<16) | (c<<8) | c ));
                    }
                }
               
                screen.unlock();
            }
        }
       
    }

初期4点をランダムに求めたあと、その4点の中間点を、4点の濃度値の平均+ノイズで求め、そこからさらに4点を使って別の点を求め、・・・と再帰的に処理を行っていきます。
乱数をテーブル化しているのは次回への布石です。次回は雲を徐々に変化させます。

actionscript3.0でストップウォッチの作成Flash

しんぶーです。

・[mixiアプリ]四則演算ゲーム携帯版のflashliteの処理構造
・[mixiアプリ]対戦リバーシの通信対戦の処理構造

に関して書こうと思ったのですが、また次の機会に。

今回は、actionscript3.0でストップウォッチの作成方法に関して。

ストップウォッチの作成方法にはenterframeとtimerの2種類があり
ざっと検索してみたところ、enterframe系の処理はサンプルソースがたくさんあったのですが、
timer系で処理しているものがみあたらなかったので書いてみました。

動作サンプル(左の四角をクリックでSTART、右の四角をクリックでSTOP)

あまり時間をかけないでサクッと書いたため、
命名や処理構造が最適化されていない部分はご了承下さい。

package {

    import flash.display.Sprite;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    import flash.events.MouseEvent;
    import flash.text.TextField;

    public class FlashTest extends Sprite {
       
        public var MiliSeconds = 0;
        public var Clock = new Timer(10);
        public var myTextField:TextField = new TextField();
        public var startBTN:Sprite = new Sprite();
        public var stopBTN:Sprite = new Sprite();
       
        public function FlashTest() {
           
            // timer text
           
            myTextField.x = 100;
            myTextField.y = 70;
            myTextField.text ="0:00:00";
            this.addChild(myTextField);
           
            // start button
           
            startBTN.graphics.beginFill(0xFFCC00);
            startBTN.graphics.drawRect(60, 5, 40, 40);
            this.addChild(startBTN);
           
            startBTN.addEventListener(MouseEvent.MOUSE_UP, ClockStart);
            startBTN.addEventListener(MouseEvent.MOUSE_OVER, mOver);
            startBTN.addEventListener(MouseEvent.MOUSE_OUT, mOut);
           
            // stop button
           
            stopBTN.graphics.beginFill(0xCCFF00);
            stopBTN.graphics.drawRect(160, 5, 40, 40);
            this.addChild(stopBTN);
           
            stopBTN.addEventListener(MouseEvent.MOUSE_UP, ClockStop);
            stopBTN.addEventListener(MouseEvent.MOUSE_OVER, mOver);
            stopBTN.addEventListener(MouseEvent.MOUSE_OUT, mOut);
           
            // clock
            Clock.addEventListener(TimerEvent.TIMER, this.Tick);
           
        }
       
        public function Tick(e:TimerEvent):void{
            MiliSeconds++;
            var s:int = Math.floor(MiliSeconds / 100);
            var minutes:int = Math.floor( s / 60);
            var seconds:int = s % 60;
            var mili:int = MiliSeconds % 100 ;
            myTextField.text = minutes + ":" + (seconds > 9 ? seconds : "0" + seconds) +":"+ (mili > 9 ? mili : "0" + mili);
        }
       
        public function mOver( e){
            e.target.alpha=0.5
        }
       
        public function mOut( e ){
            e.target.alpha =1
        }
       
        public function ClockStart(e){
            MiliSeconds = 0;
            Clock.start();
        }
       
        public function ClockStop(e){
            Clock.stop();
        }
       
    }
}



Flashとサーバー通信Flash

こんにちは ぬいぐるみです。

今回はFlashとサーバで通信をするお話です。

現在サイブリッジではいくつかのmixiアプリを開発しています。
私は直接関わっているわけではないのですがおもしろそうなので
作ってみることにしました。

構成は以下の通り
Flash:ActionScript3
サーバ:CentOS Linux
サーバ側言語:PHP
IDE:FlashCS4+FlashDevelop
Flashでは通信するために以下の方式をサポートしています。
RTMP(Routing Table Maintenance Protocol)通信
Socket通信

RTMPはバイナリで高速に通信が行えるプロトコルです。
Adobeが公式に出しているFlashMediaServerを利用することで
サーバ間通信が行えます。
ただ、ライセンスが数十万してしまうので、先ずはもう一方のSocket通信を利用しました。

Socket通信はTCP/IPで実装された通信規格でサーバとの
通信(例えばホームページを見るときなど)に使用されるプロトコルです。

PHPでは標準でSocket通信系の関数をサポートしています。

FlashではXMLSocketクラスを使用します。
このクラスではXMLだけというわけでなく、文字列であればOKです。
なので今回は連想配列が簡単に使え、両方の言語で利用できるJSONを利用しました。

JSONとはJavaScript Object Notation の略でECMAに準拠した
構造化されたデータを記述するための,テキスト・ベースのデータ記述言語の一つです。

これによって、サーバ間通信を行う際にも両方の言語で同じ変数を扱えるため
とても便利です。

ですが、実際に作ってみて以下の不都合がありました。

1.同時接続数問題


標準の設定ではLinuxの制限で同時にSocketが128までしか
開けない問題がありました。
上限値の確認方法
 
sysctl -a|grep "somax"

2.スレッドが使えない


スレッドを開いて各スレッドと1対1でFlashと対応させたかったのですが
PHPではスレッドを使うことができず、とりあえず逐次処理型で構成してみました。
(実際にやって分かったのですが逐次処理型の場合60接続で割といっぱいいっぱいでした。)

3.タイマーイベントが使えない


これは割と致命的です。
ゲームを作る上で時間制限などのイベント処理は割と必須なのです。
取り急ぎクライアント側のタイマーイベントを使っているのですが
不正などもありそうなのであまり良くない実装です。

これらを解決するために探して見つけたのがRed5(オープンソースのRTMPサーバ)です。
Red5ではFlashMediaServerとはちがい、ActionScriptではなくJavaでプログラムを書いて
Flashと連携することができます。

FlashMediaServerでは接続制限数などがありますが、Red5には無いため
1番目の問題が解決できます。
また、Javaを使用することができるため2,3の問題もクリアーすることができます。

Red5については次の機会に書きたいと思います。

mixiアプリをリリースしました。[四則演算ゲーム]Flash

こんにちは。しんぶーです。

今回は技術の話ではなく宣伝になるのですが、
先日、アルカーナ株式会社と共同でmixiアプリをリリースしました。

-------------------------------------------------------------------------------------
▼四則演算ゲーム

http://mixi.jp/view_appli.pl?id=6503

mixi_game1.jpg

・四則演算の正解数を競うゲームです。
・□に入る1~9の数字を選択して、計算式を完成しましょう
・日本語入力はオフにして下さい。数字キーでの入力も可能です。
・制限時間は30秒です

株式会社サイブリッジ(http://www.cybridge.jp/)とアルカーナ株式会社(http://arcarna.com/)のコラボレーションアプリです。

※NumLockがオン/オフになっているとテンキーが効かないことがあるようです。
■推奨環境
以下の環境で動作を確認しております。

・Windows XP/Vista
 Internet Explorer (7以降)
 Firefox (3以降)
 Google Chrome (2以降)

・Machintosh OS X(10.4.4以降)
 Firefox (3以降)

-------------------------------------------------------------------------------------

現時点で総合ランキング15位、ユーザ数は2万7000人を超えていて、嬉しい限りです。
flashを勉強していて良かったなぁと感じています。

さらに嬉しいことに現在はオススメアプリのところに掲載(右端)されていて、
その影響もあり、まだまだユーザ数が増えています。

mixi_game_osusume.jpg

インストールして頂いたユーザの方々にもっと楽しんでもらえるよう
現在追加実装を検討しております。

・計算が難しいハードモードの追加
・ランキングの細分化
・モバイル対応
・マイミクとの対戦機能
・何問解けるか延々と問いていくモードの追加

などを開発チームで検討中です。

ご要望などありましたら四則演算ゲームのコミュニティか、
ブログコメント欄までメッセージを頂けますと幸いです。

以上です。