// Author: Richard Owen // // Based on some code i saw along time ago and I can not find now to reference // Move your mouse around the stage and check out the water trail // this works well as rain drops if you want to randomly drop points onto the surface // // write as3 code here.. package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.utils.setInterval; /** * ... * @author ... */ public class Main extends Sprite { private var Water:WaterMaterial; private var intervalID:uint = 0; 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 removeEventListener(Event.ADDED_TO_STAGE, init); Water = new WaterMaterial(); stage.addChild(Water); Water.addEventListener(MouseEvent.MOUSE_DOWN, MouseClicky) Water.addEventListener(MouseEvent.MOUSE_UP, MouseRelease) Water.addEventListener(MouseEvent.MOUSE_MOVE, MouseMove) intervalID = setInterval(WaterUpdate, 10); } private function MouseClicky(event:MouseEvent):void { Water.setDrop(event.localX, event.localY); }; private function MouseMove(event:MouseEvent):void { Water.setDrop(event.localX, event.localY); }; public function WaterUpdate():void { Water.forceRefresh(); } private function MouseRelease(event:MouseEvent):void { //clearInterval(intervalID); }; } } import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.InteractiveObject; import flash.geom.Rectangle; import flash.display.Sprite; import flash.events.Event; class WaterMaterial extends Sprite { private const TestScale:Number = 5.0; private const BufWidth:uint = 93; private const BufHeight:uint = 93; private const MaxHeight:uint = 256; private const damping:Number = 64; private var Buffer1:Array = new Array(BufWidth); private var Buffer2:Array = new Array(BufWidth); private var tempBuff:Array; private var waterData:BitmapData; private var waterMap:Bitmap; public function WaterMaterial() { addEventListener(Event.ADDED_TO_STAGE, init); } public function init(e:Event): void { removeEventListener(Event.ADDED_TO_STAGE, init); //Create a bitmap to hoold the rendering data waterData = new BitmapData(BufWidth, BufHeight, true, 0x00000000); //add the water as a bitmap to the stage waterMap = new Bitmap(waterData); addChild(waterMap); // this.x = 0; this.y = 0; this.width = BufWidth * TestScale; this.height = BufHeight * TestScale; //Set all the water to a flat 0 var col:int; var row:int; for (col = 0; col < BufWidth ; col++ ) { Buffer1[col] = new Array(BufHeight); Buffer2[col] = new Array(BufHeight); for (row = 1; row < BufHeight; row++) { Buffer1[col][row] = 0; Buffer2[col][row] = 0; } } } //Add a drop of water to the pool public function setDrop(dropX:uint, dropY:uint):void { Buffer1[dropX][dropY] = MaxHeight; } //update the bitmap witht he current height private function HeightToPixel(col: int, row:int ): void { var pixelColor:uint; pixelColor = Buffer2[col][row] << 24; pixelColor = pixelColor + 0xff; waterData.setPixel32(col + 1, row + 1, pixelColor) } public function dampEdgeOfWater(): void { var col:int; var row:int; //reduce the edge pixels for (col - 0; col < BufWidth; col++){ Buffer2[col][0] = Math.floor(Buffer2[col][0] / damping); Buffer2[col][BufHeight - 1] = Math.floor(Buffer2[col][BufHeight - 1] / damping); if (Buffer2[col][0] < 0) Buffer2[col][0] = 0; if (Buffer2[col][BufHeight - 1] < 0) Buffer2[col][BufHeight - 1] = 0; //SetColor HeightToPixel(col, 0); HeightToPixel(col, BufHeight - 1); } for (row = 0; row < BufHeight; row++) { Buffer2[0][row] = Math.floor(Buffer2[0][row] / damping); Buffer2[BufWidth - 1][row] = Math.floor(Buffer2[BufWidth - 1][row] / damping); if (Buffer2[0][row] < 0) Buffer2[0][row] = 0; if (Buffer2[BufWidth - 1][row] < 0) Buffer2[BufWidth - 1][row] = 0; //SetColor HeightToPixel(0, row); HeightToPixel(BufWidth - 1, row); } } //smooth outthe ripples public function updateRipple():void { var col:int; var row:int; //Now process the inner rectangle of water for (col = 1; col < BufWidth - 1; col++ ) { for (row = 1; row < BufHeight - 1; row++) { var oldVal:int = Buffer2[col][row]; var newVal:int = Math.floor((((Buffer1[col - 1][row] + Buffer1[col + 1][row] + Buffer1[col][row + 1] + Buffer1[col][row - 1] + Buffer1[col - 1][row - 1] + Buffer1[col - 1][row + 1] + Buffer1[col + 1][row - 1] + Buffer1[col + 1][row + 1] ) / 4) - oldVal)); newVal = newVal - ( newVal / damping); if (newVal < 0) newVal = 0; //small optimisation if(oldVal != newVal) { Buffer2[col][row] = newVal; HeightToPixel(col, row); } } } dampEdgeOfWater() } //update the arrays public function forceRefresh(): void { //Update the water waterData.lock(); updateRipple(); waterData.unlock(); //Swap buffer to retain direction and velocity tempBuff = Buffer2; Buffer2 = Buffer1; Buffer1 = tempBuff; } } 2D Water Ripples - 2009-1-9