試作:マウスで壁を歪めるAct:2D

by o_healer
 DisplacementMapFilterの使い方を試し中。
 とりあえずベタな効果を作りつつ、さらにプレイヤーの移動と組み合わせてみる

 操作方法(画面を2回ほどクリックしないと十字キーがきかなかったりする)
 ・十字キー
  ・移動&ジャンプ
 ・マウス移動
  ・背景を歪ませる
♥2 | Line 276 | Modified 2010-07-27 20:19:35 | MIT License
play

ActionScript3 source code

/**
 * Copyright o_healer ( http://wonderfl.net/user/o_healer )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dTm5
 */

/*
 DisplacementMapFilterの使い方を試し中。
 とりあえずベタな効果を作りつつ、さらにプレイヤーの移動と組み合わせてみる

 操作方法(画面を2回ほどクリックしないと十字キーがきかなかったりする)
 ・十字キー
  ・移動&ジャンプ
 ・マウス移動
  ・背景を歪ませる
*/

package
{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.text.*;
    import flash.filters.*;
    import flash.ui.*;

    public class GameMain extends Sprite
    {
        //==Const==

        //干渉半径
        static public const DIS_RAD:int = 0x50;

        //プレイヤーの初期位置
        static public const PLAYER_INIT_X:int = 70;
        static public const PLAYER_INIT_Y:int = 70;

        //Utility
        static public const POS_ZERO:Point = new Point(0,0);


        //==Var==

        //背景
        public var m_BitmapData_View:BitmapData;

        //カーソル部分(背景は直接変更せず、カーソル位置に湾曲した画像を作ってかぶせる)
        public var m_Bitmap_Cursor:Bitmap;

        //コリジョン(背景+カーソルの結果を書き出して、プレイヤーのためのコリジョンとして扱う)
        public var m_BitmapData_Collision:BitmapData;

        //プレイヤー
        public var m_Player:Player;


        //==Function==

        //Static Global Access
        static private var m_Instance:GameMain;
        static public function Instance():GameMain{return m_Instance;}

        //Init
        public function GameMain() 
        {
            m_Instance = this;
            addEventListener(Event.ADDED_TO_STAGE, Init);
        }

        public function Init(e:Event=null):void
        {
            var g:Graphics;

            //Init Once
            {
                removeEventListener(Event.ADDED_TO_STAGE, init);
            }

            //コリジョン
            {
                m_BitmapData_Collision = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0xFFFFFFFF);

                //debug
                //addChild(new Bitmap(m_BitmapData_Collision));
            }

            //背景
            {
                //Create & Regist
                {
                    m_BitmapData_View = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xFFFFFF);
                    addChild(new Bitmap(m_BitmapData_View));
                }

                //Draw
                {
                    var shape_bg:Shape = new Shape();
                    g = shape_bg.graphics;

                    g.clear();
                    g.lineStyle(3, 0x000000);

                    g.drawRect(50, 50, 200, 100);

                    g.drawRect(150, 250, 200, 100);

                    g.drawRect(350, 100, 200, 100);

                    g.beginFill(0x000000, 1.0);
                    g.drawRect(0, 400, 100, 50);
                    g.endFill();

                    m_BitmapData_View.draw(shape_bg);
                }
            }

            //カーソル
            {
                //Create & Regist
                {
                    m_Bitmap_Cursor = new Bitmap(new BitmapData(2*DIS_RAD, 2*DIS_RAD, true, 0x00000000));
                    addChild(m_Bitmap_Cursor);
                }

                //フィルター
                {
                    //オフセットから色を計算
                    const calc_color:Function = function(in_GapX:int, in_GapY:int):uint{
                        var r:uint = 0x00;
                        var g:uint = 0x80 + in_GapX;
                        var b:uint = 0x80 + in_GapY;
                        return (r << 16) | (g << 8) | (b << 0);
                    };

                    //オフセット値を格納したBitmap
                    var bmd_sampling:BitmapData = new BitmapData(2*DIS_RAD, 2*DIS_RAD, false, calc_color(0,0));

                    //歪めるための計算
                    for(var y:int = 0; y < 2*DIS_RAD; y++){
                        var GapY:int = (2*DIS_RAD-1)/2 - y;
                        for(var x:int = 0; x < 2*DIS_RAD; x++){
                            var GapX:int = (2*DIS_RAD-1)/2 - x;
                            var distance:Number = Math.sqrt(GapX*GapX + GapY*GapY);

                            if(distance < DIS_RAD){
                                var ratio:Number = 1 - distance/DIS_RAD;
                                ratio = Math.sin(0.5*Math.PI * ratio);
                                ratio = 0.5 - 0.5*Math.cos(Math.PI * ratio);
                                bmd_sampling.setPixel(x, y, calc_color(GapX*ratio, GapY*ratio));
                            }
                        }
                    }

                    //実際にフィルターとして生成&セット
                    m_Bitmap_Cursor.filters = [
                        new DisplacementMapFilter(
                            bmd_sampling,//Sampling : Bitmap
                            null,//Sampling : Point
                            BitmapDataChannel.GREEN,//X
                            BitmapDataChannel.BLUE,//Y
                            0x100,//ScaleX
                            0x100,//ScaleY
                            DisplacementMapFilterMode.COLOR,//Mode
                            0x000000,//color
                            0//alpha
                        )
                    ];
                }

                //カーソルの更新
                {
                    //初期化
                    Update_Cursor();

                    //マウスの移動のたびに更新
                    stage.addEventListener(MouseEvent.MOUSE_MOVE, Update_Cursor);
                }
            }

            //プレイヤー
            {
                //Create & Regist
                {
                    m_Player = new Player(PLAYER_INIT_X, PLAYER_INIT_Y);
                    addChild(m_Player);
                }
            }
        }

        //カーソルの更新(背景からカーソル位置の画像をコピー)
        static public var copy_rect:Rectangle = new Rectangle(0,0, 2*DIS_RAD,2*DIS_RAD);
        public function Update_Cursor(e:Event=null):void{
            var lx:int = mouseX-DIS_RAD;
            var uy:int = mouseY-DIS_RAD;

            //背景の該当部分をカーソル用のBitmapにコピー
            copy_rect.x = lx;
            copy_rect.y = uy;
            m_Bitmap_Cursor.bitmapData.copyPixels(m_BitmapData_View, copy_rect, POS_ZERO);

            m_Bitmap_Cursor.x = lx;
            m_Bitmap_Cursor.y = uy;

            //さらにコリジョンも更新
            Update_Collision();
        }

        //コリジョンの更新(背景+カーソル)
        static public var mtx:Matrix = new Matrix();
        public function Update_Collision(e:Event=null):void{
            //背景を描画
            m_BitmapData_Collision.copyPixels(m_BitmapData_View, m_BitmapData_View.rect, POS_ZERO);

            //カーソルを重ねて描画
            mtx.tx = m_Bitmap_Cursor.x;
            mtx.ty = m_Bitmap_Cursor.y;
            m_BitmapData_Collision.draw(m_Bitmap_Cursor, mtx);

            //白い部分を透明にする
            m_BitmapData_Collision.threshold(m_BitmapData_Collision, m_BitmapData_Collision.rect, POS_ZERO, ">", 0xFF/2, 0x00000000, 0x000000FF);

            //さらにプレイヤーの厚みに合わせて相対的にコリジョン側の厚みを増やす(プレイヤーは1ドット分の大きさで済むように)
            const glow_filter_x:GlowFilter = new GlowFilter(0x000000,1.0, 2*Player.RAD,0, 255);
            const glow_filter_y:GlowFilter = new GlowFilter(0x000000,1.0, 0,2*Player.RAD, 255);
            m_BitmapData_Collision.applyFilter(m_BitmapData_Collision, m_BitmapData_Collision.rect, POS_ZERO, glow_filter_x);
            m_BitmapData_Collision.applyFilter(m_BitmapData_Collision, m_BitmapData_Collision.rect, POS_ZERO, glow_filter_y);
        }

        //コリジョンがあるか
        public function IsCollision(in_X:int, in_Y:int):Boolean{
            //範囲外チェック:ぶつかるようにする
            {
                if(in_X < 0){return true;}
                if(in_X >= m_BitmapData_Collision.width){return true;}
                if(in_Y < 0){return true;}
                if(in_Y >= m_BitmapData_Collision.height){return true;}
            }

            //あとは普通にコリジョンを見る
            var color:uint = m_BitmapData_Collision.getPixel32(in_X, in_Y);
            var a:uint = (color >> 24) & 0xFF;
            return a > 0xFF/2;//一定以上不透明だったらコリジョンとみなす
        }
    }
}


import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.net.*;
import flash.text.*;
import flash.filters.*;
import flash.ui.*;

//プレイヤー
internal class Player extends Sprite{

    //==Cost==

    //#Size
    static public const RAD:int = 8;

    //#Input
    static public var button_enum:int = 0;
    static public const BUTTON_L:int = button_enum++;
    static public const BUTTON_R:int = button_enum++;
    static public const BUTTON_U:int = button_enum++;
    static public const BUTTON_NUM:int = button_enum;

    //#Move
    static public const VEL_X:Number = 100.0;
    static public const VEL_Y:Number = 300.0;
    static public const ACC_Y:Number = 500.0;

    //==Var==

    //#Input
    public var m_Input:Array = new Array(BUTTON_NUM);

    //#Move
    public var m_VY:Number = 0.0;
    public var m_OnGround:Boolean = false;

    //==Function==

    //#Init
    public function Player(in_X:int, in_Y:int){
        //Pos
        {
            this.x = in_X;
            this.y = in_Y;
        }

        //Init Later (for Using "stage" etc.)
        addEventListener(Event.ADDED_TO_STAGE, Init );
    }

    public function Init(e:Event = null):void{
        var i:int;

        //Init Once Only
        {
            removeEventListener(Event.ADDED_TO_STAGE, Init );
        }

        //Create Graphic
        {
            var shape:Shape = new Shape();
            var g:Graphics = shape.graphics;

            g.lineStyle(2, 0xFF0000, 1.0);
            g.beginFill(0xFFFFFF, 1.0);
            g.drawCircle(0,0, RAD);
            g.endFill();

            addChild(shape);
        }
        
        //Input
        {
            //Init
            for(i = 0; i < BUTTON_NUM; i++){
                m_Input[i] = false;
            }

            //Listener
            stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
        }

        //Call Update
        {
            addEventListener(Event.ENTER_FRAME, Update);
        }
    }

    //#Input
    public function KeyDown(e:KeyboardEvent):void{
        switch(e.keyCode){
        case Keyboard.LEFT:        m_Input[BUTTON_L] = true;        break;
        case Keyboard.RIGHT:        m_Input[BUTTON_R] = true;        break;
        case Keyboard.UP:        m_Input[BUTTON_U] = true;        break;
        }
    }

    public function KeyUp(e:KeyboardEvent):void{
        switch(e.keyCode){
        case Keyboard.LEFT:        m_Input[BUTTON_L] = false;        break;
        case Keyboard.RIGHT:        m_Input[BUTTON_R] = false;        break;
        case Keyboard.UP:        m_Input[BUTTON_U] = false;        break;
        }
    }

    //#Update
    public function Update(e:Event):void{
        var DeltaTime:Number = 1.0 / 24.0;

        //Move
        {
           //Calc MoveVal
            var MoveX:int;
            var MoveY:int;
            {
                //X
                {
                    MoveX = 0;

                    if(m_Input[BUTTON_L]){MoveX -= VEL_X * DeltaTime;}
                    if(m_Input[BUTTON_R]){MoveX += VEL_X * DeltaTime;}
                }

                //Y
                {
                    //ジャンプ
                    if(m_OnGround && m_Input[BUTTON_U]){
                        m_VY = -VEL_Y;
                    }
                    m_OnGround = false;//毎回着地判定を行うので、着地しなかった時のためにリセット

                    //重力
                    m_VY += ACC_Y * DeltaTime;

                    MoveY = m_VY * DeltaTime;
                }
            }

            //移動アルゴリズム実行

            const BaseOffset:int = 1;//浮く量
            var Offset:int;

            var TrgX:int;
            var TrgY:int;

            var i:int;

            //Move : Up
            {
                TrgX = this.x;
                Offset = (MoveY < 0)? BaseOffset-MoveY: BaseOffset;

                for(i = 0; i < Offset; i++){
                    TrgY = this.y - 1;
                    if(GameMain.Instance().IsCollision(TrgX, TrgY)){
                        //天井にぶつかったので速度は0にする
                        m_VY = 0;
                        break;
                    }
                    this.y = TrgY;
                }
            }

            //Move : Horz
            {
                TrgX = this.x;
                TrgY = this.y;

                for(i = 0; i != MoveX; ){
                    if(i < MoveX){
                        i++;
                        TrgX++;
                    }else{
                        i--;
                        TrgX--;
                    }

                    if(GameMain.Instance().IsCollision(TrgX, TrgY)){break;}

                    this.x = TrgX;
                }
            }

            //Move : Down
            {
                TrgX = this.x;
                Offset = (MoveY > 0)? BaseOffset+MoveY: BaseOffset;

                for(i = 0; i < Offset; i++){
                    TrgY = this.y + 1;
                    if(GameMain.Instance().IsCollision(TrgX, TrgY)){
                        //接地したとみなす
                        m_OnGround = true;//ジャンプできるようにフラグオン
                        m_VY = 0;//速度リセット
                        break;
                    }
                    this.y = TrgY;
                }
            }
        }
    }
}