Shining Drops
memo
BitmapData.drawでブレンドするよりも
BitmapでBlendModeを指定した方が軽くなる。
画像のプリレンダリング+Bitmap.bitmapDataの差し替え
により描画の負荷を軽減させる。
/**
* Copyright poiasd ( http://wonderfl.net/user/poiasd )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/r4TJ
*/
package {
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.filters.GlowFilter;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.Linear;
[SWF (width = "465", height = "465", frameRate = "30", backgroundColor = "0xFFFFFF")]
public class Main extends Sprite {
private const _FOV:Number = 60;
private const _RADIUS:Number = 130;
private const _DROP_RADIUS:Number = 5;
private const _IMAGE_COUNT:int = 30;
private const _IMAGE_COLOR:uint = 0x2463A2;
private const _BLUR_SIZE:Number = 8;
private const _GRAVITY:Number = 0.3;
private var _stageWidth:int;
private var _stageHeight:int;
private var _centerX:int;
private var _centerY:int;
private var _focalLength:Number;
private var _matrix3D:Matrix3D;
private var _dropImages:Vector.<BitmapData>;
private var _dropList:Vector.<Drop>;
public function Main() {
_init();
}
private function _init():void {
stage.scaleMode = "noScale";
stage.align = "TL";
_stageWidth = stage.stageWidth;
_stageHeight = stage.stageHeight;
_centerX = _stageWidth >> 1;
_centerY = _RADIUS + 30;
// 背景の描画
graphics.beginFill(0x000000, 1);
graphics.drawRect(0, 0, _stageWidth, _stageHeight);
graphics.endFill();
// 焦点距離の計算
_focalLength = 0.5 * _stageWidth / Math.tan(0.5 * _FOV * Math.PI / 180);
_matrix3D = new Matrix3D();
// 表示画像のプリレンダリング
_dropImages = _createDropImages();
_dropList = new Vector.<Drop>();
addEventListener(Event.ENTER_FRAME, _enterFrameHandler);
}
private var _rotationY:Number = 0;
private function _enterFrameHandler(event:Event):void {
// frameごとにDropを3個作る
_createDrops(3);
// マウス位置により回転させる
_rotationY += (stage.mouseX - _rotationY) * 0.1;
_matrix3D.identity();
_matrix3D.appendRotation( - _rotationY, Vector3D.Y_AXIS);
var length:int = _dropList.length;
var i:int = _dropList.length;
while (i--) {
var drop:Drop = _dropList[i];
var v:Vector3D = drop.vertex;
if (drop.t.isPlaying) {
// tween中は角度により座標を計算
var r:Number = _RADIUS * Math.cos(drop.angle1);
v.x = r * Math.cos(drop.angle2);
v.y = _RADIUS * Math.sin(drop.angle1);
v.z = r * Math.sin(drop.angle2);
} else {
// tween後は落下運動
var vel:Vector3D = drop.velocity;
vel.x *= 0.96;
vel.y += _GRAVITY;
vel.z *= 0.96;
v.x += vel.x;
v.y += vel.y;
v.z += vel.z;
}
// 座標変換して投影する
v = _matrix3D.transformVector(v);
if (v.z < -_focalLength) break;
var t:Number = _focalLength / (_focalLength + v.z);
drop.x = _centerX + v.x * t;
drop.y = _centerY + v.y * t;
// 画面外に出たら消す
if (drop.y > _stageHeight + _DROP_RADIUS) {
removeChild(drop);
_dropList[i] = _dropList[_dropList.length - 1];
_dropList.pop();
continue;
}
// 表示サイズに合わせて画像を差し替える
var index:int = _IMAGE_COUNT * (_RADIUS - v.z) / (2 * _RADIUS) >> 0;
index = index < 0 ? 0 : index >= _IMAGE_COUNT ? _IMAGE_COUNT - 1 : index;
drop.image = _dropImages[index];
}
}
private function _createDrops(count:int):void {
for (var i:int = 0; i < count; i++) {
var drop:Drop = addChild(new Drop(_dropImages[0])) as Drop;
drop.angle1 = - Math.PI * (0.5 - Math.random() * 0.05);
drop.angle2 = Math.random() * 2 * Math.PI;
var toAngle:Number = Math.PI * (0.3 + Math.random() * 0.1);
var time:Number = 1.5 + Math.random();
var speed:Number = _RADIUS * (toAngle - drop.angle1) / (time * stage.frameRate) * 0.8;
var r:Number = - speed * Math.sin(toAngle);
// tween後の初速を予め計算しておく
var v:Vector3D = drop.velocity;
v.x = r * Math.cos(drop.angle2);
v.y = speed * Math.cos(toAngle);
v.z = r * Math.sin(drop.angle2);
// angle1をtweenさせる
drop.t = BetweenAS3.to(drop, {angle1:toAngle}, time, Linear.linear);
drop.t.play();
_dropList.push(drop);
}
}
private function _createDropImages():Vector.<BitmapData> {
// 表示に必要な最小から最大までの画像を_IMAGE_COUNTの数だけ描画する
// _IMAGE_COUNTの値が小さいと大小の切り替わりが不自然になる
var images:Vector.<BitmapData> = new Vector.<BitmapData>(_IMAGE_COUNT, true);
var shape:Shape = new Shape();
shape.filters = [new GlowFilter(_IMAGE_COLOR, 0.8, _BLUR_SIZE, _BLUR_SIZE, 2, 2), new BlurFilter(2, 2, 2)];
var g:Graphics = shape.graphics;
var offset:Number = Math.ceil(_DROP_RADIUS * _focalLength / (_focalLength - _RADIUS) + _BLUR_SIZE);
var size:Number = offset << 1;
for (var i:int = 0; i < _IMAGE_COUNT; i++) {
var z:Number = _RADIUS * (1 - 2 * i / (_IMAGE_COUNT - 1));
var r:Number = _DROP_RADIUS * _focalLength / (_focalLength + z);
g.clear();
g.beginFill(_IMAGE_COLOR);
g.drawCircle(offset, offset, r);
g.endFill();
var image:BitmapData = new BitmapData(size, size, true, 0x00000000);
image.draw(shape);
images[i] = image;
}
return images;
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.geom.Vector3D;
import org.libspark.betweenas3.tweens.ITween;
class Drop extends Sprite {
public var vertex:Vector3D = new Vector3D();
public var velocity:Vector3D = new Vector3D();
public var angle1:Number = 0;
public var angle2:Number = 0;
public var t:ITween;
private var _content:Bitmap;
public function Drop(image:BitmapData) {
_content = addChild(new Bitmap(image, "auto", true)) as Bitmap;
_content.x = - image.width * 0.5;
_content.y = - image.height * 0.5;
blendMode = BlendMode.ADD;
}
public function get image():BitmapData {
return _content.bitmapData;
}
public function set image(value:BitmapData):void {
_content.bitmapData = value;
}
}