/** * AS100本ノック * 11回目のお題は「ろうそく」 * あなたなりの「ろうそく」を表現してください。 **/ //ろうそくの影がきれいだと思ったんです。 //クリックでろうそく追加 //ドラッグでろうそく移動 package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.GradientType; import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import net.hires.debug.Stats; [SWF(width = "465", height = "465", frameRate = "30")] public class FlashTest extends Sprite { private const ZERO:Point = new Point(); private const BLUR_FILTER:BlurFilter = new BlurFilter(4, 4, 1); private const LIGHT_GRAD:Array = [0xffdaa5, 0xbbaa88, 0x000000]; private const LIGHT_GRAD_ALPHA:Array = [1,0.7,0]; private const LIGHT_GRAD_RATIO:Array = [15, 60, 255]; private const FLAME_GRAD:Array = [0xCCAA88, 0xDD8833, 0xffffff] private var scaleMat:Matrix = new Matrix(0.5, 0, 0, 0.5, 0, 0); private var candles:Vector.<Candle> = new Vector.<Candle>(); private var draggingCandle:Candle; private var shadowCanvas:Sprite = new Sprite(); private var shadowBitmap:Bitmap; private var shadowBMD:BitmapData; private var lightCanvas:Bitmap; private var lightBMD:BitmapData; private var tempLightSprite:Sprite; private var lightDrawMt:Matrix = new Matrix(); private var flameSprite:Sprite = new Sprite(); private var flames:Vector.<Sprite> = new Vector.<Sprite>(); private var lightGradMtx:Matrix = new Matrix(); private var shadowGradArr:Array = [0x333333, 0x000000]; public function FlashTest():void { createCanvases();//createCanvas stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler); stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } private function createCandle(xPos:Number, yPos:Number, h:Number, r:Number, power:Number):void { candles.push(new Candle(xPos, yPos, h, r, power)); var flameShape:Sprite = flameSprite.addChild(new Sprite()) as Sprite; flameShape.blendMode = BlendMode.ADD; flameShape.buttonMode = true; flames.push(flameShape); } private function createCanvases():void { tempLightSprite = new Sprite(); lightBMD = new BitmapData(465, 465, false, 0x000000); lightCanvas = new Bitmap(lightBMD); lightCanvas.scaleX = lightCanvas.scaleY = 1; addChild(lightCanvas); shadowBMD = new BitmapData(465 / 2, 465 / 2, true, 0x00000000); shadowBitmap = new Bitmap(shadowBMD); shadowBitmap.smoothing = true; shadowBitmap.blendMode = BlendMode.SUBTRACT; shadowBitmap.scaleX = shadowBitmap.scaleY = 2; addChild(shadowBitmap); addChild(flameSprite); } private function mouseDownHandler(evt:MouseEvent):void { var draggable:Sprite = getObjectsUnderPoint(new Point(mouseX, mouseY))[2]; if (draggable) { draggingCandle= candles[flames.indexOf(draggable)] stage.addEventListener(MouseEvent.MOUSE_MOVE, drag); stage.addEventListener(MouseEvent.MOUSE_UP, endDrag) }else { createCandle(mouseX, mouseY, 20 + Math.random() * 15, 7, 350 + Math.random() * 20); } } private function endDrag(evt:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, endDrag); stage.removeEventListener(MouseEvent.MOUSE_MOVE, drag); } private function drag(evt:MouseEvent):void { draggingCandle.x = mouseX; draggingCandle.y = mouseY; } private function enterFrameHandler(evt:Event):void { var l:int = candles.length; for (var i:int = 0; i < l; i++ ) { var t:Candle = candles[i]; if (!t.update()) { candles.splice(i, 1); flames[i].graphics.clear(); flames.splice(i, 1); l--; i--; } } loop(); } /*-----------------------------------------// //Mainloop //------------------------------------------*/ private function loop(evt:Event = null ):void { lightBMD.lock(); shadowBMD.lock(); //resetCanvases shadowCanvas.graphics.clear(); lightBMD.fillRect(lightBMD.rect, 0x000000); shadowBMD.fillRect(shadowBMD.rect, 0x00000000); var l:int = candles.length; for (var i:int = 0; i < l; i++ ) { var c:Candle = candles[i]; //shineLight tempLightSprite.graphics.clear(); lightGradMtx.createGradientBox(c.lightPower, c.lightPower, 0, 0); tempLightSprite.graphics.beginGradientFill(GradientType.RADIAL, LIGHT_GRAD, LIGHT_GRAD_ALPHA, LIGHT_GRAD_RATIO,lightGradMtx,"pad","rgb",0); tempLightSprite.graphics.drawRect(0,0,c.lightPower, c.lightPower); tempLightSprite.graphics.endFill(); lightDrawMt.createBox(1,1, 0, c.x-c.lightPower*0.5,c.y-c.lightPower*0.5); lightBMD.draw(tempLightSprite, lightDrawMt, null, BlendMode.ADD,new Rectangle(c.x-c.lightPower*0.5,c.y-c.lightPower*0.5,c.lightPower,c.lightPower), true); //flame var flame:Sprite = flames[i]; flame.graphics.clear(); flame.x = c.x; flame.y = c.y; var fCenter:Point = new Point(Math.random()*2-1-20,Math.random()*2-1-20) lightGradMtx.createGradientBox(40, 40,0,fCenter.x ,fCenter.y); flame.graphics.beginGradientFill(GradientType.RADIAL,FLAME_GRAD,[1,0.2,0],[25,100,200],lightGradMtx ); flame.graphics.drawCircle(fCenter.x+20, fCenter.y+20, 40); flame.graphics.endFill(); //castShadow for (var j:int = 0; j < l; j++ ) { if (j != i)//自分以外 { var anotherCandle:Candle = candles[j]; var a:Point = new Point(anotherCandle.x + Math.random() * 28-14, anotherCandle.y + Math.random() * 28-14);//光源の位置にゆらぎを加えとく var vec:Point = c.subtract(a); var dis:Number = vec.length; //接点ポイント計算 var tPs:Vector.<Point> = calcTangPoint(anotherCandle, c, c.radius, vec); if (anotherCandle.height > c.height) vec.normalize(dis * (c.height / (anotherCandle.height - c.height))) else vec.normalize(465);//無限になるのでステージサイズの長さに制限 //接点 var tP1:Point = tPs[0];//接点1(光源point基準) var tP2:Point = tPs[1];//接点1(光源point基準) //影のポイントを作成 var sc:Number = 1 + vec.length / dis; //MainShadow var mt:Matrix = new Matrix(); var translateX:Number = anotherCandle.x + c.x * sc; var translateY:Number= anotherCandle.y + c.y * sc; mt.createBox(sc, sc, 0, translateX, translateY); var cP1:Point = mt.transformPoint(tP1);//接点1の影がcastされるポイント(ステージ基準) var cP2:Point = mt.transformPoint(tP2);//接点2の影がcastされるポイント(ステージ基準) //SubShadow1 mt.createBox(sc, sc, 2 * Math.PI / 180, translateX, translateY); var cP1_l:Point = mt.transformPoint(tP1);//接点1の影がcastされるポイント(ステージ基準) var cP2_l:Point = mt.transformPoint(tP2);//接点2の影がcastされるポイント(ステージ基準) //SubShadow2 mt.createBox(sc, sc, -2 * Math.PI / 180, translateX, translateY); var cP1_r:Point = mt.transformPoint(tP1);//接点1の影がcastされるポイント(ステージ基準) var cP2_r:Point = mt.transformPoint(tP2);//接点2の影がcastされるポイント(ステージ基準) tP1.offset(anotherCandle.x+c.x,anotherCandle.y+c.y)//接点1(ステージ基準に変換) tP2.offset(anotherCandle.x + c.x, anotherCandle.y + c.y)//接点2(ステージ基準に変換) //drawShadowShape var alphaArr:Array = [1 - dis / (anotherCandle.lightPower * 0.8), 0]; lightGradMtx.createGradientBox(vec.length * 2, vec.length * 2, 0, c.x - vec.length, c.y - vec.length); //MainShadow shadowCanvas.graphics.beginGradientFill(GradientType.RADIAL, shadowGradArr, alphaArr, [0, 127],lightGradMtx); shadowCanvas.graphics.moveTo(tP1.x, tP1.y); shadowCanvas.graphics.lineTo(cP1.x, cP1.y); shadowCanvas.graphics.lineTo(cP2.x, cP2.y); shadowCanvas.graphics.lineTo(tP2.x, tP2.y); shadowCanvas.graphics.lineTo(tP1.x, tP1.y); shadowCanvas.graphics.endFill(); //subShadow1 shadowCanvas.graphics.beginGradientFill(GradientType.RADIAL,shadowGradArr, alphaArr, [0, 100],lightGradMtx); shadowCanvas.graphics.moveTo(tP1.x, tP1.y); shadowCanvas.graphics.lineTo(cP1_l.x, cP1_l.y); shadowCanvas.graphics.lineTo(cP2_l.x, cP2_l.y); shadowCanvas.graphics.lineTo(tP2.x, tP2.y); shadowCanvas.graphics.lineTo(tP1.x, tP1.y); shadowCanvas.graphics.endFill(); //subShadow2 shadowCanvas.graphics.beginGradientFill(GradientType.RADIAL, shadowGradArr, alphaArr, [0, 100],lightGradMtx); shadowCanvas.graphics.moveTo(tP1.x, tP1.y); shadowCanvas.graphics.lineTo(cP1_r.x, cP1_r.y); shadowCanvas.graphics.lineTo(cP2_r.x, cP2_r.y); shadowCanvas.graphics.lineTo(tP2.x, tP2.y); shadowCanvas.graphics.lineTo(tP1.x, tP1.y); shadowCanvas.graphics.endFill(); } } } shadowBMD.draw(shadowCanvas, scaleMat, null, null, null, true); shadowBMD.applyFilter(shadowBMD, shadowBMD.rect, ZERO, BLUR_FILTER); lightBMD.unlock(); shadowBMD.unlock(); } /*-----------------------------------------// //任意の点と円の接点を求める //------------------------------------------*/ private function calcTangPoint(p:Point, cP:Point, cR:Number,vec:Point):Vector.<Point> { var returnPoints:Vector.<Point> = new Vector.<Point>(); var angle:Number = Math.acos(cR / vec.length); var offsetAngle:Number = Math.atan2(vec.y, vec.x); var tP1:Point = Point.polar(cR, offsetAngle+angle); var tP2:Point = Point.polar(cR, offsetAngle-angle); tP1.offset(-p.x, -p.y); tP2.offset(-p.x, -p.y); returnPoints.push(tP1); returnPoints.push(tP2); return returnPoints; } } } import flash.geom.Point; class Candle extends Point { private var fading:Boolean = false; private var max_power:Number; private var _height:Number; public function get height():Number { return _height; } public function set height(value:Number):void {_height = value;} private var _radius:Number; public function get radius():Number { return _radius; } public function set radius(value:Number):void {_radius = value;} private var _lightPower:Number; public function get lightPower():Number { return _lightPower; } public function set lightPower(value:Number):void { _lightPower = value;} public function Candle(xPos:Number, yPos:Number, height:Number,radius:Number, power:Number):void { super(xPos, yPos); this.height = height; this.radius = radius; this.max_power = power; this.lightPower = power; } public function update():Boolean { if (!fading) { this.height -= 0.03; if (height <= 3) fading = true; }else { max_power -= (16 * Math.random() + 2); if (max_power <= 0) return false; } lightPower = max_power*(0.95+0.1*Math.random()); return true; } } forked from: 【AS100本ノック】11回目:ろうそく