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

2D Water Ripples - 2009-1-9

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
// 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;
		}
	}