In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Water Flow

Water Flow

ドラッグで流す
スペースキーでリセット

Water Flow in PORTAL2
http://www.valvesoftware.com/publications/2010/siggraph2010_vlachos_waterflow.pdf
を参考に実装

流れのベクトルに沿って移流した2枚のテクスチャを交互に補間しながら切り替えて、流れているように見せる
Get Adobe Flash player
by Nao_u 09 Apr 2011
/**
 * Copyright Nao_u ( http://wonderfl.net/user/Nao_u )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/t3uv
 */

// forked from Nao_u's 2D流体
// forked from Nao_u's 波紋+環境マップ
//   
// Water Flow
// 
// ドラッグで流す
// スペースキーでリセット
//
//
// Water Flow in PORTAL2
// http://www.valvesoftware.com/publications/2010/siggraph2010_vlachos_waterflow.pdf
// を参考に実装
//
// 流れのベクトルに沿って移流した2枚のテクスチャを交互に補間しながら切り替えて、流れているように見せる
//
//
package {      
    import flash.display.Sprite;      
    import flash.events.*;      
    [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")] 
         
    public class FlashTest extends Sprite {      
        public function FlashTest() {      
            Main = this;      
            startLoad();     
            //initialize();  

            stage.addEventListener(Event.ENTER_FRAME,update);       
            stage.addEventListener(KeyboardEvent.KEY_UP,   keyCheckUp); 
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyCheckDown); 
            stage.addEventListener(MouseEvent.MOUSE_UP,    MouseCheckUp);     
            stage.addEventListener(MouseEvent.MOUSE_DOWN,  MouseCheckDown);     

        }      
    }      
}          
import flash.display.*;  
import flash.events.* 
import flash.text.TextField;      
import flash.geom.*; 
import flash.utils.getTimer; 
import flash.ui.Keyboard; 
import flash.net.*;  

var Main:Sprite;      
var SCREEN_W:Number = 465; 
var SCREEN_H:Number = 465; 
var Text:TextField     

var Tex:ProcTex; 
var BITMAP_W:int = 465/2; 
var BITMAP_H:int = 465/2; 
var TexData: BitmapData;  
var loaderA:Loader;  
var loaderB:Loader;  
var bLoad:Boolean = false; 
         
function startLoad():void{      
    loaderA = new Loader();  
     loaderA.load( new URLRequest("http://img.f.hatena.ne.jp/images/fotolife/N/Nao_u/20090913/20091231171735.jpg") );  
    loaderA.contentLoaderInfo.addEventListener( Event.COMPLETE, loadComplete );  
} 

function loadComplete(e:Event):void {  
    loaderB = new Loader();  
    loaderB.contentLoaderInfo.addEventListener(Event.INIT, initialize);  
    loaderB.loadBytes(loaderA.contentLoaderInfo.bytes);  
} 

function initialize(e:Event):void {  
   var loader:Loader = loaderB; 
   TexData = new BitmapData(loader.width, loader.height, false);  
   TexData.draw(loader); 

    Tex = new ProcTex( BITMAP_W, BITMAP_H ); 
    Tex.Bmp.x = 0; 
    Tex.Bmp.y = 0; 

   Text = new TextField();      
   Text.text = "----";    
   Text.autoSize = "left"; 
   Main.addChild(Text);       
   bLoad = true; 
   
   var num:int = 1;
    for( var i:int=0; i<num; i++ ){
        AgentAry.push( new Agent( new Point( Rand(30,435), Rand(30,435) ) ) ); 
       AgentAry[i].update();
    }
}  

var g_ColAnm:Number = 0.0;
var g_Scale0:Number = 0.0;
var g_Scale1:Number = 0.0;
var g_Ratio0:Number = 0.0;
var g_Ratio1:Number = 0.0;
// 更新
var mxPrev:int; 
var myPrev:int; 
var Pow:Number = 1.0; 
function update(e :Event):void{      
    var time:int = getTimer();  
        
    graphicClear();   
    Tex.draw(); 
    for( var i:int=0; i<AgentAry.length; i++ ){
        AgentAry[i].update();
    }
    var mx:int = Main.mouseX * BITMAP_W / SCREEN_W; 
    var my:int = Main.mouseY * BITMAP_H / SCREEN_H; 
    Pow += Math.abs(mx - mxPrev) + Math.abs(my - myPrev);
    if( Pow > 1.0 ) Pow = 1.0;
    
    var vx:Number = (mx - mxPrev)*0.01;
    var vy:Number = (my - myPrev)*0.01;
    if( MouseData & MOUSE_LEFT ) Tex.addPower( mx, my, vx, vy, Pow );
    Pow *= 0.5;
    mxPrev = mx;
    myPrev = my;

    g_ColAnm += 1.0/12000.0;
    if( g_ColAnm > 1.0 ) g_ColAnm -= 1.0;
    g_Scale0 += 1.0/30.0;
    if( g_Scale0 > 1.0 ) g_Scale0 -= 1.0;
    g_Scale1 = g_Scale0 + 0.5;
    if( g_Scale1 > 1.0 ) g_Scale1 -= 1.0;
    
    g_Ratio0 = g_Scale0;
    if( g_Ratio0 > 0.5 ) g_Ratio0 = 1.0-g_Ratio0;
    g_Ratio0 *= 2;
    g_Ratio1 = 1.0 - g_Ratio0;
    
    //g_Ratio0 = Math.pow( g_Ratio0, 1.0/2.2);
    //g_Ratio1 = Math.pow( g_Ratio1, 1.0/2.2);
    
    if( KeyData & KEY_SPACE && !(KeyPrev & KEY_SPACE) ){
        for( i=0; i<AgentAry.length; i++ ){
            AgentAry[i].Pos.x = Rand(30,435);
            AgentAry[i].Pos.y = Rand(30,435);
            AgentAry[i].setColor();
        }
        Tex.resetFlow();
    }
    
    var endTime:int = getTimer() - time; 
    Text.text = " 生成時間:" + endTime + "[ms]";  
    KeyPrev = KeyData;  
}   

// テクスチャ生成クラス
class ProcTex{ 
    public var BmpData:BitmapData;  
    public var TmpBmpData:BitmapData;  
    public var Bmp:Bitmap; 
    public var Width:int; 
    public var Height:int; 
    public var HeightBuf:Array;  
    public var VelXBuf:Vector.<Number>;  
    public var VelYBuf:Vector.<Number>;  
    public var PressBuf:Vector.<Number>;  
    public var NoiseBuf:Vector.<int>;  
    public var HeightIdxDst:int;
    public var HeightIdxSrc0:int;
    public var HeightIdxSrc1:int;
    public var BufWidth:int; 
    public var BufHeight:int; 
        
    public var PixelBufSize:int = 256;  
    public var PixelBuf:Vector.<int>;  

    public function ProcTex( w:int, h:int ){ 
        Width = w; 
        Height = h; 
        BmpData = new BitmapData(Width, Height, false, 0xffffff);  
        Bmp = new Bitmap(BmpData);  
        Bmp.scaleX = 2.0; 
        Bmp.scaleY = 2.0; 
        Main.addChild(Bmp);       

        // 波高バッファの生成
         VelXBuf = new Vector.<Number>;  
         VelYBuf = new Vector.<Number>;  
         PressBuf = new Vector.<Number>;
         NoiseBuf = new Vector.<int>;  
        BufWidth = Width+2;
        BufHeight = Height+2;
        for( var x:int=-1; x<Width+1; x++ ){  
            for( var y:int=-1; y<Height+1; y++ ){
                var idx:int = (x+1) + (y+1)*BufWidth;
                VelXBuf.push( 0.0 );
                VelYBuf.push( 0.0 );
                PressBuf.push( 0.0 );
            }
        }
        for( x=0; x<Width*2; x++ ){  
            for( y=0; y<Height*2; y++ ){
                var c:int = Rand(0,100);
               NoiseBuf.push( c );//+c*256+c*65536 );
               //NoiseBuf.push( x*20 + y*10 );
            }
        }

        // 環境マップテクスチャバッファの生成
        PixelBuf = new Vector.<int>;
        for( x=0; x<PixelBufSize; x++ ){  
            for( y=0; y<PixelBufSize; y++ ){
                var col:int = TexData.getPixel(x, y);  
                PixelBuf.push(col);
            }
        }

        HeightIdxSrc0=0;
        HeightIdxSrc1=1;
        HeightIdxDst=2;
    } 

    public function draw():void{ 
        HeightIdxSrc0++;
        HeightIdxSrc1++;
        HeightIdxDst++;
        if( HeightIdxSrc0 > 2 ) HeightIdxSrc0 = 0;
        if( HeightIdxSrc1 > 2 ) HeightIdxSrc1 = 0;
        if( HeightIdxDst  > 2 ) HeightIdxDst  = 0;

        calcWave();     
        createBmp( HeightIdxDst );     
    }

    // 水面に力を加える
    public function addPower( px:int, py:int, vx:Number, vy:Number, pow:Number ):void{ 
        var r:int = 6;
        //var buf:Vector.<Number> = HeightBuf[HeightIdxDst];
        for( var lx:int=-r; lx<=r; lx++ ){  
            for( var ly:int=-r; ly<=r; ly++ ){
                const d:Number = Math.sqrt(lx*lx+ly*ly); 
                if (d < r) { 
                    var p:Number = Math.cos(Math.PI/2 * d/r); 
                    var x:int = px + lx;  
                    var y:int = py + ly;  
                    if( x < 0 || x >= Width || y < 0 || y >= Height ) break;
                    var idx:int = (x+1) + (y+1)*BufWidth;
                 //   buf[idx] += pow * p;
                    
                    // 上下左右の圧力差から、速度を更新
                    VelXBuf[idx] += vx;
                    VelYBuf[idx] += vy;
                    
                } 
            }
        }
    }

    public function getNoiseTex( x:Number, y:Number ):int{
          var idx:int = int(x) + int(y)*BufWidth;
          while( idx >= BufWidth*BufWidth ) idx -= BufWidth*BufWidth;
          while( idx < 0 ) idx += BufWidth*BufWidth;
          
          var c0:Number = NoiseBuf[idx];
          var c1:Number = NoiseBuf[idx+1];
          var c2:Number = NoiseBuf[idx+BufWidth];
          var c3:Number = NoiseBuf[idx+BufWidth+1];
          
          //x -= 0.5;
          //y -= 0.5;
          var dx:Number = x - int(x);
          var dy:Number = y - int(y);
          var iidx:Number = 1.0-dx;
          var iidy:Number = 1.0-dy;
          return (c0*iidx + c1*dx) * iidy + (c2*iidx + c3*dx) * dy;
    }

    // 水面画像の生成
    public function createBmp( idx:int ):void{ 
        var col:Number, colU:Number, colL:Number;
        var vecX0:Number, vecY0:Number, vecX1:Number, vecY1:Number, vx:Number, vy:Number; 
        var ix:int, iy:int, idx0:int, idx1:int;
        BmpData.lock();
        for( var x:int=0; x<Width-10; x++ ){  
            for( var y:int=0; y<Height-10; y++ ){
                
                idx = (x+1) + (y+1)*BufWidth;
                vx = VelXBuf[idx];
                vy = VelYBuf[idx];
                vecX0 = vx * 800.0 * g_Scale0;
                vecY0 = vy * 800.0 * g_Scale0;
                vecX1 = vx * 800.0 * g_Scale1;
                vecY1 = vy * 800.0 * g_Scale1;
                
      //          idx0 = (x/2-vecX0) + int((y/2-vecY0))*BufWidth;
      //          idx1 = (y/2-vecY1) + int((x/2-vecX1))*BufWidth;
      //          while( idx0 >= BufWidth*BufWidth-100 ) idx0 -= BufWidth*BufWidth-100;
      //          while( idx1 >= BufWidth*BufWidth-100 ) idx1 -= BufWidth*BufWidth-100;
      //          while( idx0 < 0 ) idx0 += BufWidth*BufWidth;
      //          while( idx1 < 0 ) idx1 += BufWidth*BufWidth;
      //          var c0:Number = NoiseBuf[idx0];
      //          var c1:Number = NoiseBuf[idx1];
                var c0:Number = getNoiseTex(x/1.0-vecX0, y/1.00-vecY0);
                var c1:Number = getNoiseTex((x+10)/1.0-vecX1, (y+10)/1.0-vecY1);
                
                //if( x>200 && y>200 ) c1 = 128;
                var c:Number = c1 * g_Ratio1 + c0 * g_Ratio0;
                //c = Math.pow( c/256, 1.0/2.2) * 256;

                //c += g_ColAnm*255;
                if( c >= 255 ) c = 255;
                
                if( c > 128 ) c = 255-c;
                //if( c < 16 ) c += 16;
                c *= 2;
                
                var cc:int = c;
                
                var cr:int = c;
                //cc *= g_Ratio1;
                BmpData.setPixel(x, y, cc+cc*256+cr*65536); 
               
               // BmpData.setPixel(x, y, int(VelXBuf[idx]*399+128)+int(VelYBuf[idx]*399+128)*256); 
  
            }
        }    
        BmpData.unlock();
    }

    // 流れを計算
    public function calcWave():void{ 
        var idx:int, idxU:int, idxD:int, idxL:int, idxR:int;
        var x:int, y:int;
        
        // 圧力を計算
        var px:Number, py:Number;
        for( x=0; x<Width-2; x++ ){  
            for( y=0; y<Height-2; y++ ){
                idx  = (x+1) + (y+1)*BufWidth;
                idxU = idx-BufWidth;
                idxD = idx+BufWidth;
                idxL = idx-1;
                idxR = idx+1;
                
                px = (VelXBuf[idxL] - VelXBuf[idxR]);
                py = (VelYBuf[idxU] - VelYBuf[idxD]);
                PressBuf[idx] = (px + py) * 0.99;
            }
        }            

        // 速度を更新
        for( x=0; x<Width-2; x++ ){  
            for( y=0; y<Height-2; y++ ){
                idx  = (x+1) + (y+1)*BufWidth;
                idxU = idx-BufWidth;
                idxD = idx+BufWidth;
                idxL = idx-1;
                idxR = idx+1;
                
                // 上下左右の圧力差から、速度を更新
                VelXBuf[idx] += (PressBuf[idxL] - PressBuf[idxR]) * 0.25;
                VelYBuf[idx] += (PressBuf[idxU] - PressBuf[idxD]) * 0.25;
             }
        }            
        
        // 粘性を計算
        var tmp:Number = 0;
        var mul:Number = 0.2;
        for( x=0; x<Width-2; x++ ){  
            for( y=0; y<Height-2; y++ ){
                idx  = (x+1) + (y+1)*BufWidth;
                idxU = idx-BufWidth;
                idxD = idx+BufWidth;
                idxL = idx-1;
                idxR = idx+1;
                
                tmp = VelXBuf[idx];
                VelXBuf[idx] +=
                (
                    (VelXBuf[idxL] - tmp) +
                    (VelXBuf[idxR] - tmp) +
                    (VelXBuf[idxU] - tmp) +
                    (VelXBuf[idxD] - tmp)
                ) * mul;

                tmp = VelYBuf[idx];
                VelYBuf[idx] +=
                (
                    (VelYBuf[idxL] - tmp) +
                    (VelYBuf[idxR] - tmp) +
                    (VelYBuf[idxU] - tmp) +
                    (VelYBuf[idxD] - tmp)
                ) * mul;
                
            }
         }
    } 
    
    public function resetFlow():void{ 
        for( var x:int=0; x<Width-2; x++ ){  
            for( var y:int=0; y<Height-2; y++ ){
                var idx:int  = (x+1) + (y+1)*BufWidth;
                PressBuf[idx] = 0;
                VelXBuf[idx] = 0;
                VelYBuf[idx] = 0;
            }
        }            
    }
}

var AgentAry:Vector.<Agent> = new Vector.<Agent>; 

class Agent{  
    public var Sp:Sprite;  
    public var Pos:Point = new Point; 
    public var Color:int;
    public function Agent( p:Point ){  
        Sp=new Sprite();    
        Pos = p;  
        Sp.x = Pos.x;  
        Sp.y = Pos.y;
        setColor( ); 
        Main.stage.addChild(Sp);   
    }  

    public function setColor( ):void{
        var ix:int = Pos.x;
        var iy:int = Pos.y;
        Color = (ix*256 / 456)*256 + 
        ((iy*256 / 456)<<16)+ 128;
    }

    public function update( ):void{
        var x:int = (Pos.x+1)/2;
        var y:int = (Pos.y+1)/2;
        if( x<0 ) x=0;
        if( y<0 ) y=0;
        if( x>BITMAP_W ) x=BITMAP_W-1;
        if( y>BITMAP_H ) y=BITMAP_H-1;
        var idx:int  = (x) + (y)*Tex.BufWidth;

        Pos.x += Tex.VelXBuf[idx]*50;
        Pos.y += Tex.VelYBuf[idx]*50;
        

        Sp.x = Pos.x;  
        Sp.y = Pos.y; 
        
        if( Pos.x < 9 ) { Pos.x = 8; }
        if( Pos.y < 9 ) { Pos.y = 8; }
        if( Pos.x > SCREEN_W-9 ) { Pos.x = SCREEN_W-10; }
        if( Pos.y > SCREEN_H-9 ) { Pos.y = SCREEN_H-10; }
        
     //   drawCircle( Pos.x, Pos.y, 5, Color );
     //   drawCircle( Pos.x, Pos.y, 2, Color );
         drawRect( Pos.x, Pos.y, 3, 3, Color );
   }

}  


function graphicClear():void{   
    Main.graphics.clear();    
}   

function drawCircle( x:Number, y:Number, size:Number, col:int ):void{     
    //Main.graphics.lineStyle(0,0x000000);        
    Main.graphics.beginFill( col, 1 );     
    Main.graphics.drawCircle( x, y, size );     
    Main.graphics.endFill();     
}    


function drawRect(sx:Number, sy:Number, ex:Number, ey:Number, col:int ):void{     
//    Main.graphics.lineStyle( 0, col );
    Main.graphics.beginFill( col );
    Main.graphics.drawRect( sx, sy, ex, ey );
    Main.graphics.endFill();
}

function Rand( min:Number, max:Number ):Number{
    return  Math.random() * (max-min) + min;
}

var MOUSE_LEFT:int = 0x01;    
var MOUSE_LEFT_TRG:int = 0x02;    
var MouseData:int;    
function MouseCheckDown(event:MouseEvent):void{    
    MouseData |= MOUSE_LEFT;    
    MouseData |= MOUSE_LEFT_TRG;    
}             

function MouseCheckUp(event:MouseEvent):void{    
    MouseData &= ~MOUSE_LEFT;    
}             

function MouseUpdate():void{    
    MouseData &= ~MOUSE_LEFT_TRG;    
}  



var KEY_UP:int    = 0x01;
var KEY_DOWN:int  = 0x02;
var KEY_LEFT:int  = 0x04;
var KEY_RIGHT:int = 0x08;
var KEY_SPACE:int = 0x10;
var KeyData:int;
var KeyPrev:int;
function keyCheckDown(event:KeyboardEvent):void { 
    switch (event.keyCode){ 
        case Keyboard.UP:	    KeyData |= KEY_UP; break;
        case Keyboard.DOWN:	    KeyData |= KEY_DOWN; break;
        case Keyboard.LEFT:    KeyData |= KEY_LEFT; break;
        case Keyboard.RIGHT:   KeyData |= KEY_RIGHT; break;
        case Keyboard.SPACE:   KeyData |= KEY_SPACE; break;
    }
} 
function keyCheckUp(event:KeyboardEvent):void { 
    switch (event.keyCode){ 
        case Keyboard.UP:      KeyData &= ~KEY_UP; break;
        case Keyboard.DOWN:    KeyData &= ~KEY_DOWN; break;
        case Keyboard.LEFT:    KeyData &= ~KEY_LEFT; break;
        case Keyboard.RIGHT:   KeyData &= ~KEY_RIGHT; break;
        case Keyboard.SPACE:   KeyData &= ~KEY_SPACE; break;
    }
}