戻る


カスタムswfの作成(おまけ)

APuzzleは画像のほかにSpriteを継承したswfファイルを使うことができます。 対象となるのはスタート、完成のメッセージパネルとパズルに使用する画像です。 画像の代わりにswfを使う場合は制約が多くなっています。 おまけということで詳細は控えますが、カスタムswfを作る場合の簡単なサンプルコードを紹介します。

必要なもの

カスタムswfを作成するにはAPuzzleで使用しているインターフェイスを実装する必要があります。 APuzzleの配布サイトからソースコードをダウンロードしてください。 FlashDevelopのプロジェクト形式になっていますが、必要なのはソースのみなのでコマンドラインのコンパイラがあれば作成できます。 コンパイラの導入方法について、メモにまとめてあるので参考にしてください。

コンパイルオプション

compiler.source-pathにAPuzzle/srcを追加します。 use-networkオプションは、サーバ用のAPuzzle.swfで使用する場合はtrueにします。 ローカル用のAPuzzle_local.swfで使用する場合はfalseにします。

以下はコンパイル設定ファイルの例です。

<flex-config>
    <compiler>
        <source-path append="true">
            <path-element>ソースの解凍パス/APuzzle/src</path-element>
        </source-path>
    </compiler>
    <use-network>false</use-network>
    ...
    その他設定
</flex-config>

パズルの画像

パズルの画像を自作swfでアニメーションさせることができます。 イベントが検出できないのでユーザーの操作に反応させることはできません。 アニメーションの特定フレームやランダムで効果音を入れることは可能です。 APNGでは表現しきれないアニメーションや、省サイズでアニメーションを組み立てたいときどうぞ。

swfはIImageFramesを実装し、Spriteを継承したクラスをコンパイルして作ります。 APuzzleはIImageFrames.currentFrameBmpが返すBitmapDataを描画に使います。 currentFrameBmpで得られる画像はSpriteの表示内容である必要はありません。 ただし、BitmapDataはBitmapクラスを経由してSpriteに追加しないと動いてくれないようです。

APuzzleはENTER_FRAMEイベントが発生するたびにupdateCurrentFrameを呼びます。 画像を更新しtrueを返せばパズルの表示に反映されます。

swfがリソースを読み込むタイミングをAPuzzleに知らせる手段はありません。 APuzzleがswfをロードした時点で全てのリソースが揃うようにするために、リソースはembeddedで埋め込んでください。

APuzzleは重いflashなので画像代わりのswfも重さに注意してください。

以下のコードは埋め込んだtest.jpgをスクロールさせるサンプルです。 コンパイルしてできたswfをパラメータxmlファイルのargs.imageに指定すると、画像の変わりに使用できます。 swfのデフォルトサイズに関わらず、frameWidthとframeHeightはtest.jpgのサイズになります。

package 
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.net.URLRequest;
    
    public class APuzzleImageSample extends Sprite implements IImageFrames
    {
        public static const INTERVAL:int = 5;
        
        [Embed(source='test.jpg')]
        private var SampleImage : Class;
        
        private var _bitmap:Bitmap;
        private var _currentFrameBmp:BitmapData;
        private var _count:int;
        private var _scrollHeight:int;
        
        public function APuzzleImageSample():void
        {
            _bitmap = new SampleImage();
            _currentFrameBmp = new BitmapData(_bitmap.width, _bitmap.height, false);
            var rect:Rectangle = new Rectangle(0, 0, _bitmap.width, _bitmap.height);
            _currentFrameBmp.copyPixels(_bitmap.bitmapData, rect, new Point() );
            
            // addChildしないと更新が有効にならない?
            addChild(new Bitmap(_currentFrameBmp) );
        }
        
        public function get currentFrameBmp():BitmapData
        {
            return _currentFrameBmp;
        }
        
        public function get frameWidth():int
        {
            return _currentFrameBmp.width;
        }
        
        public function get frameHeight():int
        {
            return _currentFrameBmp.height;
        }
        
        public function resetCurrentFrame():void
        {
        }
        
        public function updateCurrentFrame():Boolean
        {
            _count++;
            if (_count < INTERVAL)
                return false;
            
            _count = 0;
            _scrollHeight++;
            if (height <= _scrollHeight)
            {
                _scrollHeight = 0;
            }
            
            var rect:Rectangle = new Rectangle();
            var point:Point = new Point();
            
            rect.width = width;
            rect.height = height - _scrollHeight;
            point.y = _scrollHeight;
            _currentFrameBmp.copyPixels(_bitmap.bitmapData, rect, point);
            rect.y = height - _scrollHeight;
            rect.height = _scrollHeight;
            point.y = 0;
            _currentFrameBmp.copyPixels(_bitmap.bitmapData, rect, point);
            
            return true;
        }
    }
}

メッセージパネル

スタートメッセージパネルと完成メッセージパネルで自作swfを使うことができます。 メッセージパネル表示中はパズルの画像がクリックされたら次の処理に進むようになっています。 マウスクリックのイベントは処理しないことを推奨します。

swfはIMessagePanelを実装し、Spriteを継承したクラスをコンパイルして作ります。 ADDED_TO_STAGEイベントを受けてアニメーションを開始するようにします。 ADDED_TO_STAGEイベント後にSprite.stageプロパティが有効になるので、 初期化のタイミングには注意してください。

パズル画像のどこかがクリックされたらexitメソッドが呼ばれます。 swfはフェードアウトなどの演出をしてからAPuzzleEvent.MESSAGE_COMPLETEイベントを送出してください。

swfがリソースを読み込むタイミングをAPuzzleに知らせる手段はありません。 APuzzleがswfをロードした時点で全てのリソースが揃うようにするために、リソースはembeddedで埋め込んでください。

以下のコードはスタートメッセージパネルのサンプルです。 コンパイルしてできたswfをパラメータxmlファイルのargs.message.startに指定すると、画像の変わりに使用できます。 「クリックでパズル開始」というメッセージを帯状に並べて表示します。 クリックされたら帯の位置を下ろしていき、画面外に出たらパズルを操作できるようになります。

package 
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    public class APuzzleMessageSample extends Sprite implements IMessagePanel
    {
        public static const MESSAGE:String = "クリックでパズル開始   ";
        public static const FONT_SIZE:int = 48;
        public static const TEXT_FIELD_MARGIN:Number = 1.2;
        public static const SCROLL_SPEED:int = 3;
        public static const DROP_SPEED:int = 10;
        
        private var _parameter:ParameterLoader;
        private var _resource:ResourceLoader;
        
        private var _strBitmapData:BitmapData;
        private var _bitmap:Bitmap;
        private var _drawX:int;
        private var _drawNum:int;
        private var _calledExit:Boolean;
        private var _soundComplete:Boolean;
        
        public function APuzzleMessageSample():void
        {
            addEventListener(Event.ADDED_TO_STAGE, onAdded);
            
            // メッセージをBitmapDataに描いて取っておく
            var textField:TextField = new TextField();
            var textFormat:TextFormat = new TextFormat();
            textFormat.size = FONT_SIZE;
            textField.defaultTextFormat = textFormat;
            textField.autoSize = TextFieldAutoSize.LEFT;
            textField.text = MESSAGE;
            
            _strBitmapData = new BitmapData(textField.textWidth * TEXT_FIELD_MARGIN, textField.textHeight, true, 0x00000000);
            _strBitmapData.draw(textField);
        }
        
        public function initialize(parameter:ParameterLoader, resource:ResourceLoader):void
        {
            _parameter = parameter;
            _resource = resource;
        }
        
        public function exit():void
        {
            _calledExit = true;
            
            var sound:Sound = _resource.startSound;
            if (sound != null)
            {
                var soundChannel:SoundChannel = sound.play();
                soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
            }
            else
            {
                _soundComplete = true;
            }
        }
        
        private function onAdded(event:Event):void
        {
            // 横幅いっぱい、縦位置中央にメッセージ表示用のbitmapを作成
            // Sprite.stageプロパティが使用できるのはADDED_TO_STAGE以降
            _bitmap = new Bitmap(new BitmapData(stage.stageWidth, _strBitmapData.height, true, 0x00000000) );
            addChild(_bitmap);
            this.x = 0;
            this.y = (stage.stageHeight - _strBitmapData.height) / 2;
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
            
            _drawNum = Math.ceil(stage.stageWidth / _strBitmapData.width) + 1;
        }
        
        private function onSoundComplete(event:Event):void
        {
            _resource.releaseStartSound();
            _soundComplete = true;
        }
        
        private function onEnterFrame(event:Event):void
        {
            _drawX -= SCROLL_SPEED;
            if (_drawX <= -_strBitmapData.width)
                _drawX = 0;
            
            var rect:Rectangle = new Rectangle(0, 0, _strBitmapData.width, _strBitmapData.height);
            var point:Point = new Point();
            
            // メッセージを横に並べて描画
            for (var i:int = 0; i < _drawNum; i++)
            {
                point.x = _drawX + _strBitmapData.width * i;
                _bitmap.bitmapData.copyPixels(_strBitmapData, rect, point);
            }
            
            // exitメソッドが呼ばれた後はメッセージを下ろしていく
            if (_calledExit)
            {
                this.y += DROP_SPEED;
                if ( (stage.stageHeight < this.y) && _soundComplete) // 画面外に消えたら終了
                {
                    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
                    dispatchEvent(new Event(APuzzleEvent.MESSAGE_COMPLETE) );
                }
            }
        }
    }
}

戻る