flash on 2011-5-6
/**
* Copyright shuu_ice ( http://wonderfl.net/user/shuu_ice )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/x0Vt
*/
/**
* Copyright k0rin ( http://wonderfl.net/user/k0rin )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/AafT
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.text.*;
import flash.utils.*;
[SWF(width="465", height="465", frameRate="30")]
public class Firework extends Sprite
{
// 火花
private var position:Vector.<Number> = new Vector.<Number>();
private var velocity:Vector.<Number> = new Vector.<Number>();
private var diameters:Vector.<Number> = new Vector.<Number>();
private var sparkNumber:Number = 0;
private var projectedPosition:Vector.<Number> = new Vector.<Number>();
private var uvts:Vector.<Number> = new Vector.<Number>();
private var sparkBitmapDatas:Vector.<BitmapData> = new Vector.<BitmapData>();
private var sparkBitmapDataRectangles:Vector.<Rectangle> = new Vector.<Rectangle>();
private const SPARK_COLOR:uint = 0x664020;
private const GRAVITY:Number = 0.02;
private const AIR_RESISTANCE:Number = 0.999;
// 1フレームに放出する火花の数
private const EMIT_SPARK_NUMBER:int = 1;
// フレームバッファ
private var displayBuffer:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false);
private var splitBuffer:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false);
private const NUMBER_OF_SPLIT_FRAME:int = 10;
private var perspective:PerspectiveProjection = new PerspectiveProjection();
private var projectionMatrix:Matrix3D = new Matrix3D();
private var viewRotationY:Number = 0;
private var viewRotationX:Number = 30;
private const CENTER_X:Number = stage.stageWidth / 2;
private const CENTER_Y:Number = stage.stageHeight * 0.65;
private var stats:TextField = new TextField();
public function Firework()
{
addChild(new Bitmap(displayBuffer));
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
// 火花のプリレンダリング
var bitmapData:BitmapData = new BitmapData(1, 1, true, SPARK_COLOR);
for (var diameter:int = 1; diameter <= 32; diameter++) {
bitmapData = new BitmapData(diameter, diameter, true, 0);
g.clear();
g.beginFill(SPARK_COLOR);
var radius:Number = diameter / 2;
g.drawCircle(radius, radius, radius);
g.endFill();
bitmapData.draw(shape);
sparkBitmapDatas.push(bitmapData);
sparkBitmapDataRectangles.push(bitmapData.rect);
// bitmapData.rectはアクセサで重いのでキャッシュしておく。これにより30%ほど高速化
}
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
// stage.addEventListener(MouseEvent.CLICK, function (e:Event):void {
// emitSpark();
// });
}
private function enterFrameHandler(e:Event):void
{
displayBuffer.fillRect(displayBuffer.rect, 0);
setupProjectionMatrix();
//var startTime:uint = getTimer();
//startTime = getTimer();
for (var i:int = 0; i < NUMBER_OF_SPLIT_FRAME; i++) {
const FACTOR:Number = 0.06;
//setupProjectionMatrix();
/*viewRotationY += (getTimer() * 0.01 + (-(CENTER_X - mouseX) * 0.4) - viewRotationY) * FACTOR;
viewRotationX += ((27 + (CENTER_Y - mouseY) * 0.15) - viewRotationX) * FACTOR;*/
for (var j:int = 0; j < 1; j++) {
emitSpark();
}
updateSparks();
renderSparks();
}
}
private function setupProjectionMatrix():void
{
perspective.fieldOfView = 60;
projectionMatrix.identity();
projectionMatrix.appendRotation(viewRotationY, Vector3D.Y_AXIS);
projectionMatrix.appendRotation(viewRotationX, Vector3D.X_AXIS);
projectionMatrix.appendTranslation(0, 0, perspective.focalLength);
projectionMatrix.append(perspective.toMatrix3D());
//correctMatrix3DMultiplyBug(projectionMatrix);
}
private function createSpark(x:Number, y:Number, z:Number, vx:Number, vy:Number, vz:Number, diameter:Number):void
{
position.push(x, y, z);
velocity.push(vx, vy, vz);
diameters.push(diameter);
sparkNumber++;
}
private function emitSpark():void
{
//var azimuth:Number = Math.random() * 2 * Math.PI;
var azimuth:Number = 0.5 * 2 * Math.PI;
//var vy:Number = -(Math.random() * 0.06 + 0.94);
var vy:Number = -(0.5 * 0.06 + 0.94);
var vx:Number = Math.sqrt(1 - vy * vy) * Math.cos(azimuth);
var vz:Number = Math.sqrt(1 - vy * vy) * Math.sin(azimuth);
var speed:Number = Math.random() * 2.3 + 0.7;
vx *= speed;
vy *= speed;
vz *= speed;
var diameter:Number = 3 + Math.random() * 3;
const FIREWORK_HEIGHT:Number = 40;
createSpark(0, -FIREWORK_HEIGHT, 0, vx, vy, vz, diameter);
}
private function updateSparks():void
{
for (var i:int = 0; i < sparkNumber; ) {
var xIndex:int = i * 3;
var yIndex:int = xIndex + 1;
var zIndex:int = xIndex + 2;
velocity[yIndex] += GRAVITY;
if (position[yIndex] + velocity[yIndex] > 0) {
/*velocity[yIndex] = -velocity[yIndex] * 0.2;
position[xIndex] += velocity[xIndex];
position[yIndex] += velocity[yIndex];
position[zIndex] += velocity[zIndex];*/
// 小さい火花に分割
const DIAMETER_MIN:Number = 2;
if (diameters[i] > DIAMETER_MIN) {
/*var diameterLimit:Number = diameters[i];
while (1) {
var diameter:Number = Math.random() * (4 - DIAMETER_MIN) + DIAMETER_MIN;
diameterLimit -= diameter;
if (diameterLimit < 0) {
break;
}
var azimuth:Number = Math.random() * 2 * Math.PI;
var r:Number = Math.random() * 2;
createSpark(position[xIndex], position[yIndex], position[zIndex],
(velocity[xIndex] + Math.cos(azimuth) * r) * 0.5,
velocity[yIndex],
(velocity[zIndex] + Math.sin(azimuth) * r) * 0.5,
diameter);
}*/
}
position[xIndex] = position[sparkNumber * 3 - 3];
position[yIndex] = position[sparkNumber * 3 - 2];
position[zIndex] = position[sparkNumber * 3 - 1];
velocity[xIndex] = velocity[sparkNumber * 3 - 3];
velocity[yIndex] = velocity[sparkNumber * 3 - 2];
velocity[zIndex] = velocity[sparkNumber * 3 - 1];
diameters[i] = diameters[sparkNumber - 1];
position.pop();
position.pop();
position.pop();
velocity.pop();
velocity.pop();
velocity.pop();
diameters.pop();
sparkNumber--;
continue;
}
position[xIndex] += velocity[xIndex];
position[yIndex] += velocity[yIndex];
position[zIndex] += velocity[zIndex];
i++;
}
}
private function renderSparks():void
{
splitBuffer.fillRect(splitBuffer.rect, 0x000000);
Utils3D.projectVectors(projectionMatrix, position, projectedPosition, uvts);
// ループ外に追い出したら30%以上高速化
var p:Point = new Point();
var focalLength:Number = perspective.focalLength;
for (var i:int = 0; i < sparkNumber; i++) {
// パーティクルの大きさ = 1/z * focalLength * pixel
var diameter:int = uvts[i * 3 + 2] * focalLength * diameters[i] + 0.5;
// (i * 2) => (i << 1)で10%ほど高速化
p.x = CENTER_X + projectedPosition[(i << 1) ] - diameter / 2;
p.y = CENTER_Y + projectedPosition[(i << 1) + 1] - diameter / 2;
splitBuffer.copyPixels(sparkBitmapDatas[diameter], sparkBitmapDataRectangles[diameter], p);
}
displayBuffer.draw(splitBuffer, null, null, BlendMode.ADD);
}
}
}